Eu preciso obter uma contagem de linhas de um arquivo grande (centenas de milhares de linhas) em python. Qual é a maneira mais eficiente em termos de memória e tempo?
No momento eu faço:
def file_len(fname):
with open(fname) as f:
for i, l in enumerate(f):
pass
return i + 1
é possível fazer melhor?
python
text-files
line-count
SilentGhost
fonte
fonte
enumerate(f, 1)
e abandonar oi + 1
?Respostas:
Você não pode ficar melhor do que isso.
Afinal, qualquer solução precisará ler o arquivo inteiro, descobrir quantas
\n
você possui e retornar esse resultado.Você tem uma maneira melhor de fazer isso sem ler o arquivo inteiro? Não tenho certeza ... A melhor solução será sempre ligada à E / S, o melhor que você pode fazer é garantir que você não use memória desnecessária, mas parece que você a cobriu.
fonte
Uma linha, provavelmente bem rápida:
fonte
Acredito que um arquivo mapeado de memória será a solução mais rápida. Eu tentei quatro funções: a função postada pelo OP (
opcount
); uma iteração simples sobre as linhas no arquivo (simplecount
); readline com um arquivo mapeado na memória (mmap) (mapcount
); e a solução de leitura de buffer oferecida por Mykola Kharechko (bufcount
).Executei cada função cinco vezes e calculei o tempo médio de execução para um arquivo de texto de 1,2 milhão de linhas.
Windows XP, Python 2.5, 2 GB de RAM, processador AMD de 2 GHz
Aqui estão os meus resultados:
Edit : números para Python 2.6:
Portanto, a estratégia de leitura de buffer parece ser a mais rápida para Windows / Python 2.6
Aqui está o código:
fonte
wccount()
é o mais rápido gist.github.com/0ac760859e614cd03652Eu tive que postar isso em uma pergunta semelhante até que minha pontuação de reputação subisse um pouco (graças a quem me bateu!).
Todas essas soluções ignoram uma maneira de tornar essa execução consideravelmente mais rápida, usando a interface não armazenada em buffer (bruta), usando bytearrays e fazendo seu próprio buffer. (Isso só se aplica no Python 3. No Python 2, a interface bruta pode ou não ser usada por padrão, mas no Python 3, você usará o Unicode como padrão.)
Usando uma versão modificada da ferramenta de temporização, acredito que o código a seguir é mais rápido (e marginalmente mais pitônico) do que qualquer uma das soluções oferecidas:
Usando uma função de gerador separada, isso executa um smidge mais rápido:
Isso pode ser feito completamente com expressões de geradores in-line usando itertools, mas fica muito estranho:
Aqui estão os meus horários:
fonte
wccount
nesta tabela para awc
ferramenta de shell do subprocesso ?rawincount
solução menos esquisita usando embufgen = iter(partial(f.raw.read, 1024*1024), b'')
vez de combinartakewhile
erepeat
.Você pode executar um subprocesso e executar
wc -l filename
fonte
Aqui está um programa python para usar a biblioteca de multiprocessamento para distribuir a contagem de linhas entre máquinas / núcleos. Meu teste melhora a contagem de um arquivo de linha de 20 milhões de 26 segundos para 7 segundos usando um servidor Windows 64 de 8 núcleos. Nota: não usar o mapeamento de memória torna as coisas muito mais lentas.
fonte
Uma solução bash de uma linha semelhante a esta resposta , usando a
subprocess.check_output
função moderna :fonte
wc -l
leva aproximadamente 5 segundos.shell=True
é ruim para a segurança, é melhor evitá-lo.Eu usaria o método de objeto de arquivo do Python
readlines
, da seguinte maneira:Isso abre o arquivo, cria uma lista de linhas no arquivo, conta o comprimento da lista, salva isso em uma variável e fecha o arquivo novamente.
fonte
xreadlines
está obsoleto desde a versão 2.3, pois apenas retorna um iterador.for line in file
é a substituição declarada. Veja: docs.python.org/2/library/stdtypes.html#file.xreadlinesfonte
Aqui está o que eu uso, parece bastante limpo:
ATUALIZAÇÃO: Isso é marginalmente mais rápido do que usar python puro, mas ao custo do uso de memória. O subprocesso bifurcará um novo processo com o mesmo espaço de memória que o processo pai enquanto ele executa seu comando.
fonte
:-)
Esta é a coisa mais rápida que eu encontrei usando python puro. Você pode usar a quantidade de memória desejada configurando o buffer, embora 2 ** 16 pareça ser um ponto ideal no meu computador.
Encontrei a resposta aqui Por que a leitura de linhas do stdin é muito mais lenta em C ++ que no Python? e ajustou um pouquinho. É uma leitura muito boa para entender como contar linhas rapidamente, embora
wc -l
ainda seja 75% mais rápido do que qualquer outra coisa.fonte
Eu obtive uma pequena melhoria (4-8%) com esta versão que reutiliza um buffer constante para evitar qualquer sobrecarga de memória ou GC:
Você pode brincar com o tamanho do buffer e talvez ver uma pequena melhoria.
fonte
Resposta de Kyle
provavelmente é o melhor, uma alternativa para isso é
Aqui está a comparação do desempenho de ambos
fonte
Solução de uma linha:
Meu trecho:
fonte
os.system()
em variável e pós-processá-la de qualquer maneira.Apenas para concluir os métodos acima, tentei uma variante com o módulo fileinput:
E passou um arquivo de linhas de 60mil para todos os métodos acima mencionados:
É uma pequena surpresa para mim que a entrada de arquivo seja tão ruim e tenha uma escala muito pior do que todos os outros métodos ...
fonte
Quanto a mim, essa variante será a mais rápida:
razões: armazenar em buffer mais rapidamente do que ler linha por linha e
string.count
também é muito rápidofonte
Este código é mais curto e mais claro. Provavelmente é a melhor maneira:
fonte
Eu modifiquei o caso do buffer assim:
Agora também os arquivos vazios e a última linha (sem \ n) são contados.
fonte
Que tal isso
fonte
count = max(enumerate(open(filename)))[0]
fonte
enumerate()
é a contagem de início, de acordo com docs.python.org/2/library/functions.html#enumeratefonte
fonte
Se alguém quiser obter a contagem de linhas mais barata no Python no Linux, recomendo este método:
file_path pode ser o caminho abstrato do arquivo ou o caminho relativo. Espero que isso possa ajudar.
fonte
Que tal agora?
fonte
Que tal esse one-liner:
Demora 0,003 segundos usando esse método para cronometrar em um arquivo de linha 3900
fonte
fonte
Método simples:
1)
2)
3)
fonte
o resultado da abertura de um arquivo é um iterador, que pode ser convertido em uma sequência com um comprimento:
isso é mais conciso do que seu loop explícito e evita o
enumerate
.fonte
Você pode usar o
os.path
módulo da seguinte maneira:, onde
Filename
é o caminho absoluto do arquivo.fonte
os.path
?Se o arquivo puder caber na memória,
fonte