Tenho notado um desempenho muito fraco ao usar iterrows de pandas.
Isso é algo experimentado por outras pessoas? É específico para iterrows e essa função deve ser evitada para dados de um determinado tamanho (estou trabalhando com 2-3 milhões de linhas)?
Essa discussão no GitHub me levou a acreditar que isso é causado pela mistura de dtypes no dataframe, no entanto, o exemplo simples abaixo mostra que está lá mesmo ao usar um dtype (float64). Isso leva 36 segundos na minha máquina:
import pandas as pd
import numpy as np
import time
s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
start = time.time()
i=0
for rowindex, row in dfa.iterrows():
i+=1
end = time.time()
print end - start
Por que as operações vetorizadas como aplicar são muito mais rápidas? Eu imagino que deve haver alguma iteração linha por linha acontecendo lá também.
Não consigo descobrir como não usar iterrows no meu caso (vou guardar para uma pergunta futura). Portanto, gostaria de saber se você tem conseguido evitar essa iteração de forma consistente. Estou fazendo cálculos com base em dados em dataframes separados. Obrigado!
--- Editar: versão simplificada do que eu quero executar foi adicionada abaixo ---
import pandas as pd
import numpy as np
#%% Create the original tables
t1 = {'letter':['a','b'],
'number1':[50,-10]}
t2 = {'letter':['a','a','b','b'],
'number2':[0.2,0.5,0.1,0.4]}
table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)
#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])
#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
t2info = table2[table2.letter == row['letter']].reset_index()
table3.ix[row_index,] = optimize(t2info,row['number1'])
#%% Define optimization
def optimize(t2info, t1info):
calculation = []
for index, r in t2info.iterrows():
calculation.append(r['number2']*t1info)
maxrow = calculation.index(max(calculation))
return t2info.ix[maxrow]
fonte
apply
NÃO é vetorizado.iterrows
é ainda pior, pois encaixa tudo (que 'o desempenho difere comapply
). Você só deve usariterrows
em muito poucas situações. IMHO nunca. Mostre com o que você está realmente fazendoiterrows
.DatetimeIndex
emTimestamps
(foi implementado no espaço do python), e isso foi muito melhorado no master.Respostas:
Geralmente,
iterrows
só deve ser usado em casos muito, muito específicos. Esta é a ordem geral de precedência para o desempenho de várias operações:Usar uma rotina Cython personalizada geralmente é muito complicado, então vamos pular isso por enquanto.
1) A vetorização é SEMPRE, SEMPRE a primeira e melhor escolha. No entanto, há um pequeno conjunto de casos (geralmente envolvendo uma recorrência) que não podem ser vetorizados de maneiras óbvias. Além disso, em um pequeno
DataFrame
, pode ser mais rápido usar outros métodos.3)
apply
geralmente pode ser manipulado por um iterador no espaço Cython. Isso é tratado internamente pelos pandas, embora dependa do que está acontecendo dentro daapply
expressão. Por exemplo,df.apply(lambda x: np.sum(x))
será executado muito rapidamente, embora, claro,df.sum(1)
seja ainda melhor. No entanto, algo comodf.apply(lambda x: x['b'] + 1)
será executado no espaço do Python e, conseqüentemente, é muito mais lento.4)
itertuples
não encaixar os dados em umSeries
. Ele apenas retorna os dados na forma de tuplas.5)
iterrows
ENCAIXE os dados em umSeries
. A menos que você realmente precise disso, use outro método.6) Atualizar um quadro vazio uma única linha de cada vez. Eu vi esse método ser muito usado. É de longe o mais lento. Provavelmente é um lugar comum (e razoavelmente rápido para algumas estruturas python), mas a
DataFrame
faz um bom número de verificações na indexação, portanto, será sempre muito lento atualizar uma linha por vez. Muito melhor para criar novas estruturas econcat
.fonte
itertuples
é mais rápido do queapply
:(pd.DataFrame.apply
geralmente é mais lento do queitertuples
. Além disso, vale a pena considerar as compreensões de lista,,map
os mal nomeadosnp.vectorize
enumba
(em nenhuma ordem particular) para cálculos não vetorizáveis , por exemplo, veja esta resposta .As operações de vetor em Numpy e pandas são muito mais rápidas do que as operações escalares em Python vanilla por vários motivos:
Pesquisa de tipo amortizado : Python é uma linguagem tipada dinamicamente, portanto, há sobrecarga de tempo de execução para cada elemento em um array. No entanto, Numpy (e, portanto, pandas) realizam cálculos em C (geralmente via Cython). O tipo da matriz é determinado apenas no início da iteração; essa economia por si só é uma das maiores vitórias.
Melhor cache : a iteração sobre um array C é amigável ao cache e, portanto, muito rápida. Um DataFrame do pandas é uma "tabela orientada a colunas", o que significa que cada coluna é realmente apenas um array. Portanto, as ações nativas que você pode executar em um DataFrame (como somar todos os elementos em uma coluna) terão poucos erros de cache.
Mais oportunidades para paralelismo : Um array C simples pode ser operado por meio de instruções SIMD. Algumas partes do Numpy habilitam o SIMD, dependendo da CPU e do processo de instalação. Os benefícios do paralelismo não serão tão dramáticos quanto a digitação estática e melhor armazenamento em cache, mas eles ainda são uma vitória sólida.
Moral da história: use as operações vetoriais em Numpy e pandas. Elas são mais rápidas do que as operações escalares em Python pela simples razão de que essas operações são exatamente o que um programador C teria escrito à mão de qualquer maneira. (Exceto que a noção de array é muito mais fácil de ler do que loops explícitos com instruções SIMD incorporadas.)
fonte
Esta é a maneira de resolver seu problema. Tudo isso é vetorizado.
fonte
Outra opção é usar
to_records()
, que é mais rápido do que ambositertuples
eiterrows
.Mas, para o seu caso, há muito espaço para outros tipos de melhorias.
Aqui está minha versão final otimizada
Teste de referência:
Código completo:
A versão final é quase 10x mais rápida do que o código original. A estratégia é:
groupby
para evitar a comparação repetida de valores.to_records
para acessar objetos numpy.records brutos.fonte
Sim, Pandas itertuples () é mais rápido do que iterrows (). você pode consultar a documentação: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html
"Para preservar dtypes durante a iteração nas linhas, é melhor usar itertuples () que retorna namedtuples dos valores e que geralmente é mais rápido do que iterows."
fonte
Detalhes neste vídeo
Benchmark
fonte