Necesito obtener un recuento 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?
Por el momento lo hago:
def file_len(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
¿Es posible hacerlo mejor?
python
text-files
line-count
SilentGhost
fuente
fuente
enumerate(f, 1)
y deshacerse de lai + 1
?Respuestas:
No hay nada 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.¿Tiene una mejor manera de hacerlo sin leer el archivo completo? No estoy seguro ... La mejor solución siempre estará vinculada a E / S, lo mejor que puede hacer es asegurarse de no usar memoria innecesaria, pero parece que tiene eso cubierto.
fuente
Una línea, probablemente bastante rápida:
fuente
Creo que un archivo mapeado en memoria será la solución más rápida. Probé 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 buffer 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:
Editar : números para Python 2.6:
Entonces, la estrategia de lectura del búfer parece ser la más rápida para Windows / Python 2.6
Aquí está el código:
fuente
wccount()
es el gist.github.com/0ac760859e614cd03652Tuve que publicar esto en una pregunta similar hasta que mi puntaje de reputación aumentó 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, usando la interfaz sin búfer (sin procesar), usando bytearrays y haciendo su propio almacenamiento en búfer. (Esto solo se aplica en Python 3. En Python 2, la interfaz en bruto puede o no usarse de manera predeterminada, pero en Python 3, usted usará Unicode de manera predeterminada).
Usando una versión modificada de la herramienta de sincronización, creo que el siguiente código es más rápido (y marginalmente más pitónico) que cualquiera de las soluciones ofrecidas:
Usando una función de generador separada, esto ejecuta un smidge más rápido:
Esto se puede hacer completamente con expresiones de generadores en línea usando itertools, pero se ve bastante extraño:
Aquí están mis horarios:
fuente
wccount
en esta tabla para lawc
herramienta de shell de subproceso ?rawincount
solución tenga un aspecto menos extraño utilizando enbufgen = iter(partial(f.raw.read, 1024*1024), b'')
lugar de combinartakewhile
yrepeat
.Podría ejecutar un subproceso y ejecutar
wc -l filename
fuente
Aquí hay un programa de Python para usar la biblioteca de multiprocesamiento para distribuir el recuento de líneas entre máquinas / núcleos. Mi prueba mejora contando un archivo de línea de 20 millones de 26 segundos a 7 segundos usando un servidor Windows 64 de 8 núcleos. Nota: no usar el mapeo de memoria hace las cosas mucho más lentas.
fuente
Una solución bash de una línea similar a esta respuesta , utilizando la
subprocess.check_output
función moderna :fuente
wc -l
tarda ~ 5 segundos.shell=True
es malo para la seguridad, es mejor evitarlo.Usaría el método de objeto de archivo de Python
readlines
, de la siguiente manera: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 cierra el archivo nuevamente.
fuente
xreadlines
ha quedado en desuso desde 2.3, ya que solo devuelve un iterador.for line in file
es el reemplazo indicado. Ver: docs.python.org/2/library/stdtypes.html#file.xreadlinesfuente
Esto es lo que uso, parece bastante limpio:
ACTUALIZACIÓN: Esto es marginalmente más rápido que usar Python puro pero a costa del uso de memoria. El subproceso bifurcará un nuevo proceso con la misma huella de memoria que el proceso principal mientras ejecuta su comando.
fuente
:-)
Esto es lo más rápido que he encontrado usando Python puro. Puede usar la cantidad de memoria que desee configurando el búfer, aunque 2 ** 16 parece ser un punto ideal en mi computadora.
Encontré la respuesta aquí ¿Por qué leer líneas de stdin es mucho más lento en C ++ que Python? y lo ajusté solo un poquito. Es una muy buena lectura para entender cómo contar líneas rápidamente, aunque
wc -l
todavía es aproximadamente un 75% más rápido que cualquier otra cosa.fuente
Obtuve una pequeña mejora (4-8%) con esta versión que reutiliza un búfer constante, por lo que debería evitar cualquier memoria o sobrecarga del GC:
Puede jugar con el tamaño del búfer y tal vez ver una pequeña mejora.
fuente
La respuesta de Kyle
es probablemente el mejor, una alternativa para esto es
Aquí está la comparación del rendimiento de ambos
fuente
Solución de una línea:
Mi fragmento:
fuente
os.system()
variable y procesarla de todos modos.Solo para completar los métodos anteriores, probé una variante con el módulo de entrada de archivo:
Y pasó un archivo de líneas de 60mil a todos los métodos mencionados anteriormente:
Me sorprende un poco que la entrada de archivos sea tan mala y escale mucho peor que todos los demás métodos ...
fuente
En cuanto a mí, esta variante será la más rápida:
razones: almacenamiento en búfer más rápido que leer línea por línea y
string.count
también es muy rápidofuente
Este código es más corto y más claro. Probablemente sea la mejor manera:
fuente
He modificado el caso del búfer así:
Ahora también se cuentan los archivos vacíos y la última línea (sin \ n).
fuente
Que hay de esto
fuente
count = max(enumerate(open(filename)))[0]
fuente
enumerate()
es el recuento de inicio de acuerdo con docs.python.org/2/library/functions.html#enumeratefuente
fuente
Si uno quiere obtener el recuento de líneas a bajo costo en Python en Linux, recomiendo este método:
file_path puede ser una ruta de archivo abstracta o una ruta relativa. Espero que esto pueda ayudar.
fuente
¿Qué tal esto?
fuente
¿Qué tal este one-liner:
Toma 0.003 segundos usando este método para cronometrarlo en un archivo de 3900 líneas
fuente
fuente
Método simple:
1)
2)
3)
fuente
El resultado de abrir un archivo es un iterador, que se puede convertir en una secuencia, que tiene una longitud:
esto es más conciso que su ciclo explícito y evita el
enumerate
.fuente
Puede usar el
os.path
módulo de la siguiente manera:, donde
Filename
está la ruta absoluta del archivo.fuente
os.path
?Si el archivo puede caber en la memoria, entonces
fuente