with - write to a file in python
¿Cómo obtener una cuenta de línea barata en Python? (30)
¿Por qué no leer las primeras 100 y las últimas 100 líneas y estimar la longitud promedio de la línea, luego dividir el tamaño total del archivo entre esos números? Si no necesita un valor exacto, esto podría funcionar.
Necesito obtener un conteo de líneas de un archivo grande (cientos de miles de líneas) en python. ¿Cuál es la forma más eficiente de memoria y tiempo?
En el momento que hago:
def file_len(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
¿Es posible hacerlo mejor?
¿Qué tal este one-liner:
file_length = len(open(''myfile.txt'',''r'').read().split(''/n''))
Toma 0.003 segundos usando este método para cronometrarlo en un archivo de 3900 líneas
def c():
import time
s = time.time()
file_length = len(open(''myfile.txt'',''r'').read().split(''/n''))
print time.time() - s
¿Qué tal esto?
import fileinput
import sys
counter=0
for line in fileinput.input([sys.argv[1]]):
counter+=1
fileinput.close()
print counter
Aquí hay un programa de Python para usar la biblioteca de multiprocesamiento para distribuir el conteo de líneas a través de máquinas / núcleos. Mi prueba mejora el conteo de un archivo de 20 millones de líneas de 26 segundos a 7 segundos utilizando un servidor Windows 64 de 8 núcleos. Nota: no usar la asignación de memoria hace las cosas mucho más lentas.
import multiprocessing, sys, time, os, mmap
import logging, logging.handlers
def init_logger(pid):
console_format = ''P{0} %(levelname)s %(message)s''.format(pid)
logger = logging.getLogger() # New logger at root level
logger.setLevel( logging.INFO )
logger.handlers.append( logging.StreamHandler() )
logger.handlers[0].setFormatter( logging.Formatter( console_format, ''%d/%m/%y %H:%M:%S'' ) )
def getFileLineCount( queues, pid, processes, file1 ):
init_logger(pid)
logging.info( ''start'' )
physical_file = open(file1, "r")
# mmap.mmap(fileno, length[, tagname[, access[, offset]]]
m1 = mmap.mmap( physical_file.fileno(), 0, access=mmap.ACCESS_READ )
#work out file size to divide up line counting
fSize = os.stat(file1).st_size
chunk = (fSize / processes) + 1
lines = 0
#get where I start and stop
_seedStart = chunk * (pid)
_seekEnd = chunk * (pid+1)
seekStart = int(_seedStart)
seekEnd = int(_seekEnd)
if seekEnd < int(_seekEnd + 1):
seekEnd += 1
if _seedStart < int(seekStart + 1):
seekStart += 1
if seekEnd > fSize:
seekEnd = fSize
#find where to start
if pid > 0:
m1.seek( seekStart )
#read next line
l1 = m1.readline() # need to use readline with memory mapped files
seekStart = m1.tell()
#tell previous rank my seek start to make their seek end
if pid > 0:
queues[pid-1].put( seekStart )
if pid < processes-1:
seekEnd = queues[pid].get()
m1.seek( seekStart )
l1 = m1.readline()
while len(l1) > 0:
lines += 1
l1 = m1.readline()
if m1.tell() > seekEnd or len(l1) == 0:
break
logging.info( ''done'' )
# add up the results
if pid == 0:
for p in range(1,processes):
lines += queues[0].get()
queues[0].put(lines) # the total lines counted
else:
queues[0].put(lines)
m1.close()
physical_file.close()
if __name__ == ''__main__'':
init_logger( ''main'' )
if len(sys.argv) > 1:
file_name = sys.argv[1]
else:
logging.fatal( ''parameters required: file-name [processes]'' )
exit()
t = time.time()
processes = multiprocessing.cpu_count()
if len(sys.argv) > 2:
processes = int(sys.argv[2])
queues=[] # a queue for each process
for pid in range(processes):
queues.append( multiprocessing.Queue() )
jobs=[]
prev_pipe = 0
for pid in range(processes):
p = multiprocessing.Process( target = getFileLineCount, args=(queues, pid, processes, file_name,) )
p.start()
jobs.append(p)
jobs[0].join() #wait for counting to finish
lines = queues[0].get()
logging.info( ''finished {} Lines:{}''.format( time.time() - t, lines ) )
Creo que un archivo mapeado en memoria será la solución más rápida. opcount
cuatro funciones: la función publicada por el OP ( opcount
); una iteración simple sobre las líneas en el archivo ( simplecount
); readline con un archivo mapeado en memoria (mmap) ( mapcount
); y la solución de lectura de búfer ofrecida por Mykola Kharechko ( bufcount
).
Ejecuté cada función cinco veces y calculé el tiempo de ejecución promedio para un archivo de texto de 1.2 millones de líneas.
Windows XP, Python 2.5, 2 GB de RAM, procesador AMD de 2 GHz
Aquí están mis resultados:
mapcount : 0.465599966049
simplecount : 0.756399965286
bufcount : 0.546800041199
opcount : 0.718600034714
Edición : números para Python 2.6:
mapcount : 0.471799945831
simplecount : 0.634400033951
bufcount : 0.468800067902
opcount : 0.602999973297
Así que la estrategia de lectura de búfer parece ser la más rápida para Windows / Python 2.6
Aquí está el código:
from __future__ import with_statement
import time
import mmap
import random
from collections import defaultdict
def mapcount(filename):
f = open(filename, "r+")
buf = mmap.mmap(f.fileno(), 0)
lines = 0
readline = buf.readline
while readline():
lines += 1
return lines
def simplecount(filename):
lines = 0
for line in open(filename):
lines += 1
return lines
def bufcount(filename):
f = open(filename)
lines = 0
buf_size = 1024 * 1024
read_f = f.read # loop optimization
buf = read_f(buf_size)
while buf:
lines += buf.count(''/n'')
buf = read_f(buf_size)
return lines
def opcount(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
counts = defaultdict(list)
for i in range(5):
for func in [mapcount, simplecount, bufcount, opcount]:
start_time = time.time()
assert func("big_file.txt") == 1209138
counts[func].append(time.time() - start_time)
for key, vals in counts.items():
print key.__name__, ":", sum(vals) / float(len(vals))
El resultado de abrir un archivo es un iterador, que se puede convertir a una secuencia, que tiene una longitud:
with open(filename) as f:
return len(list(f))
esto es más conciso que su bucle explícito, y evita la enumerate
.
En cuanto a mí esta variante será la más rápida:
#!/usr/bin/env python
def main():
f = open(''filename'')
lines = 0
buf_size = 1024 * 1024
read_f = f.read # loop optimization
buf = read_f(buf_size)
while buf:
lines += buf.count(''/n'')
buf = read_f(buf_size)
print lines
if __name__ == ''__main__'':
main()
razones: el almacenamiento en búfer más rápido que leer línea por línea y string.count
también es muy rápido
Este código es más corto y claro. Es probablemente la mejor manera:
num_lines = open(''yourfile.ext'').read().count(''/n'')
Esto es lo más rápido que he encontrado usando python puro. Puedes usar la cantidad de memoria que quieras configurando el búfer, aunque 2 ** 16 parece ser un punto dulce en mi computadora.
from functools import partial
buffer=2**16
with open(myfile) as f:
print sum(x.count(''/n'') for x in iter(partial(f.read,buffer), ''''))
Encontré la respuesta aquí ¿Por qué leer líneas de stdin es mucho más lento en C ++ que en Python? y lo pellizcó un poquito. Es una muy buena lectura para entender cómo contar líneas rápidamente, aunque wc -l
es aproximadamente un 75% más rápido que cualquier otra cosa.
Esto es lo que uso, parece bastante limpio:
import subprocess
def count_file_lines(file_path):
"""
Counts the number of lines in a file using wc utility.
:param file_path: path to file
:return: int, no of lines
"""
num = subprocess.check_output([''wc'', ''-l'', file_path])
num = num.split('' '')
return int(num[0])
ACTUALIZACIÓN: Esto es ligeramente más rápido que usar python puro pero a costa del uso de la memoria. Subproceso procesará un nuevo proceso con la misma huella de memoria que el proceso primario mientras ejecuta su comando.
He modificado el caso de búfer de esta manera:
def CountLines(filename):
f = open(filename)
try:
lines = 1
buf_size = 1024 * 1024
read_f = f.read # loop optimization
buf = read_f(buf_size)
# Empty file
if not buf:
return 0
while buf:
lines += buf.count(''/n'')
buf = read_f(buf_size)
return lines
finally:
f.close()
Ahora también se cuentan los archivos vacíos y la última línea (sin / n).
No puedes ser mejor que eso.
Después de todo, cualquier solución tendrá que leer el archivo completo, averiguar cuántos /n
tiene y devolver ese resultado.
¿Tienes una mejor manera de hacerlo sin leer todo el archivo? No estoy seguro ... La mejor solución siempre estará vinculada a la E / S, lo mejor que puede hacer es asegurarse de no usar memoria innecesaria, pero parece que tiene todo eso cubierto.
Obtuve una pequeña mejora (4-8%) con esta versión que reutiliza un búfer constante, por lo que debería evitar cualquier sobrecarga de memoria o GC:
lines = 0
buffer = bytearray(2048)
with open(filename) as f:
while f.readinto(buffer) > 0:
lines += buffer.count(''/n'')
Puedes jugar con el tamaño del búfer y tal vez ver una pequeña mejora.
Otra posibilidad:
import subprocess
def num_lines_in_file(fpath):
return int(subprocess.check_output(''wc -l %s'' % fpath, shell=True).strip().split()[0])
Podría ejecutar un subproceso y ejecutar wc -l filename
import subprocess
def file_len(fname):
p = subprocess.Popen([''wc'', ''-l'', fname], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result, err = p.communicate()
if p.returncode != 0:
raise IOError(err)
return int(result.strip().split()[0])
Que hay de esto
def file_len(fname):
counts = itertools.count()
with open(fname) as f:
for _ in f: counts.next()
return counts.next()
Si uno quiere obtener el recuento de líneas a bajo precio en Python en Linux, recomiendo este método:
import os
print os.popen("wc -l file_path").readline().split()[0]
file_path puede ser tanto una ruta de archivo abstracta como una ruta relativa. Espero que esto pueda ayudar.
Similar:
lines = 0
with open(path) as f:
for line in f:
lines += 1
Solo para completar los métodos anteriores, probé una variante con el módulo fileinput:
import fileinput as fi
def filecount(fname):
for line in fi.input(fname):
pass
return fi.lineno()
Y pasó un archivo de 60mil líneas a todos los métodos mencionados anteriormente:
mapcount : 6.1331050396
simplecount : 4.588793993
opcount : 4.42918205261
filecount : 43.2780818939
bufcount : 0.170812129974
Es una pequeña sorpresa para mí que la entrada de archivos sea tan mala y se escale mucho peor que todos los otros métodos ...
Tuve que publicar esto en una pregunta similar hasta que mi puntuación de reputación saltó un poco (¡gracias a quien me golpeó!).
Todas estas soluciones ignoran una forma de hacer que esto funcione considerablemente más rápido, es decir, mediante el uso de la interfaz sin búfer (en bruto), el uso de bytearrays y la creación de su propio almacenamiento en búfer. (Esto solo se aplica en Python 3. En Python 2, la interfaz sin formato puede o no usarse de manera predeterminada, pero en Python 3, se establecerá un valor predeterminado en Unicode).
Al usar una versión modificada de la herramienta de sincronización, creo que el siguiente código es más rápido (y ligeramente más pitón) que cualquiera de las soluciones ofrecidas:
def rawcount(filename):
f = open(filename, ''rb'')
lines = 0
buf_size = 1024 * 1024
read_f = f.raw.read
buf = read_f(buf_size)
while buf:
lines += buf.count(b''/n'')
buf = read_f(buf_size)
return lines
Usando una función de generador separada, esto ejecuta un poco más rápido:
def _make_gen(reader):
b = reader(1024 * 1024)
while b:
yield b
b = reader(1024*1024)
def rawgencount(filename):
f = open(filename, ''rb'')
f_gen = _make_gen(f.raw.read)
return sum( buf.count(b''/n'') for buf in f_gen )
Esto se puede hacer completamente con expresiones de generadores en línea usando itertools, pero se ve bastante raro:
from itertools import (takewhile,repeat)
def rawincount(filename):
f = open(filename, ''rb'')
bufgen = takewhile(lambda x: x, (f.raw.read(1024*1024) for _ in repeat(None)))
return sum( buf.count(b''/n'') for buf in bufgen )
Aquí están mis horarios:
function average, s min, s ratio
rawincount 0.0043 0.0041 1.00
rawgencount 0.0044 0.0042 1.01
rawcount 0.0048 0.0045 1.09
bufcount 0.008 0.0068 1.64
wccount 0.01 0.0097 2.35
itercount 0.014 0.014 3.41
opcount 0.02 0.02 4.83
kylecount 0.021 0.021 5.05
simplecount 0.022 0.022 5.25
mapcount 0.037 0.031 7.46
Una línea, probablemente bastante rápida:
num_lines = sum(1 for line in open(''myfile.txt''))
Una solución de bash de una línea similar a esta respuesta , utilizando la función moderna subprocess.check_output
:
def line_count(file):
return int(subprocess.check_output(''wc -l {}''.format(file), shell=True).split()[0])
Yo usaría readlines
método de objeto de archivo de Python, como sigue:
with open(input_file) as foo:
lines = len(foo.readlines())
Esto abre el archivo, crea una lista de líneas en el archivo, cuenta la longitud de la lista, la guarda en una variable y vuelve a cerrar el archivo.
una solución de línea
import os
os.system("wc -l filename")
mi fragmento
os.system (''wc -l * .txt'')
0 bar.txt
1000 command.txt
3 test_file.txt
1003 total
count = max(enumerate(open(filename)))[0]
La respuesta de kyle
num_lines = sum(1 for line in open(''my_file.txt''))
Es probablemente lo mejor, una alternativa para esto es
num_lines = len(open(''my_file.txt'').read().splitlines())
Aquí está la comparación de rendimiento de ambos
In [20]: timeit sum(1 for line in open(''Charts.ipynb''))
100000 loops, best of 3: 9.79 µs per loop
In [21]: timeit len(open(''Charts.ipynb'').read().splitlines())
100000 loops, best of 3: 12 µs per loop
def count_text_file_lines(path):
with open(path, ''rt'') as file:
line_count = sum(1 for _line in file)
return line_count
def file_len(full_path):
""" Count number of lines in a file."""
f = open(full_path)
nr_of_lines = sum(1 for line in f)
f.close()
return nr_of_lines
def line_count(path):
count = 0
with open(path) as lines:
for count, l in enumerate(lines, start=1):
pass
return count
print open(''file.txt'', ''r'').read().count("/n") + 1