Afaste-se das expressões regulares. Eles são lentos (em todos os idiomas) e são muito mais do que precisamos aqui, o que equivale a simples comparações de substring.
- Pegue uma substring de 0:20 como a primeira chave
- Pegue a substring de 21:22 (single char) como o resultado booleano da segunda chave
- O valor dessa combinação deve ser um inteiro que você acabou de incrementar a cada vez que você a vê.
Eu implementei essa ideia abaixo em Python:
data = {}
with open("file", "r") as f:
for line in f:
key = line[0:20]
allowed = int(line[21:22] != "A")
if not key in data:
data[key] = [0,0]
data[key][allowed] += 1
for key, val in data.items():
print('%s,%d,%d' % (key, val[0], val[1]))
Nenhuma ideia de como isso funciona, mas dê uma chance. Se isso for mais lento, converta-o para C ++ (um pouco mais de um PITA, e é por isso que estou usando o Python!) E isso deve rasgar seus dados. Não é uma programação difícil, mas é o que é necessário para uma velocidade ideal.
Um pouco de refatoração, mais difícil de portar, a menos que você tenha um equivalente a defaultdict
:
from collections import defaultdict
data = defaultdict(lambda: [0,0])
with open("file", "r") as f:
for line in f:
key = line[0:20]
allowed = int(line[21:22] != "A")
data[key][allowed] += 1
for key, val in data.items():
print('%s,%d,%d' % (key, val[0], val[1]))
E uma implementação em Python de um híbrido de steeldriver e minhas ideias. Esta é provavelmente a mais eficiente de memória que você obterá e está usando a comparação de substring em vez de uma extração de regex, por isso deve ser nippy. É necessária uma entrada classificada.
last = ""
score = [0,0]
with open("file", "r") as f:
for line in f:
key = line[0:20]
allowed = int(line[21:22] != "A")
if key and key != last:
print '%s,%d,%d' % (last, score[0], score[1])
score = [0,0]
last = key
score[allowed] += 1
print '%s,%d,%d' % (last, score[0], score[1])
Avaliação comparativa
No interesse de obter algo disso testado (para minha própria curiosidade, tanto quanto qualquer outra coisa), decidi fazer um pequeno benchmarking em um arquivo de registro de 2.400.000 que abrange 2400 datas separadas.
Eu usei o seguinte script Python para gerar um arquivo grande com terminações aleatórias Permitir / Negar:
import itertools, datetime, random
CHOICES = ['Allow', 'Deny']
with open("file", "w") as f:
for _ in itertools.repeat(None, 2400):
epoch = random.randint(1, 1404226041)
d = datetime.datetime.fromtimestamp(epoch)
print d
dstr = d.strftime('%b %d %Y %X')
for _ in itertools.repeat(None, 1000):
f.write('%s %s\n' % (dstr, CHOICES[random.randint(0,1)]))
Isso foi cerca de mil vezes mais rápido que o equivalente ao Bash (consulte o log de revisão) e nos fornece um arquivo de log diversificado para ser usado. Ele não está trocado, portanto, os dois benchmarks que precisam de entrada agrupada (3 e 4 abaixo) estão usando uma versão separada separada ( sort file > file-sorted
, que levou 0m4.253s para ser concluída).
- Meu primeiro: 0m1.544s
- Meu refatorador com
defaultdict
: 0m1.413s - Classificação de
awk
: 0m5.168s + 0m4.253s da Steeldriver - Minha reimplementação do Python da classificação # 3: 0m1.489s + 0m4.253s
Repeti a geração com 2,4 milhões de datas distintas (deveria levar os meus dois primeiros aos seus limites). Esse tipo levou 0m6.999s. Eu também adicionei pypy
timings para as versões do Python.
- 0m11.589s (0m7.080s em
pypy
) - 0m11.307s (0m7.087s em
pypy
) - 0m8.652s + 0m6.999s
- 0m6.586s + 0m6.999s (0m1.566s em
pypy
)
Análise e resultados
- Em pequenos conjuntos de chaves, 1 e 2 têm melhor desempenho.
pypy
ajuda em conjuntos de chaves maiores. - 4 é mais rápido que 3
awk
em grande parte porque não estamos regexando - 4 é o mais rápido e tem o menor footprint, mas apenas se os dados forem pré-classificados
- 2 é mais rápido se tivermos dados misturados A classificação externa é muito lenta.