Como iterar sobre linhas em um DataFrame no Pandas?
Resposta: NÃO * !
A iteração nos pandas é um antipadrão e é algo que você só deve fazer quando tiver esgotado todas as outras opções. Você não deve usar nenhuma função com " iter
" em seu nome por mais de alguns milhares de linhas ou terá que se acostumar com muita espera.
Deseja imprimir um DataFrame? Use DataFrame.to_string()
.
Deseja calcular alguma coisa? Nesse caso, procure métodos nesta ordem (lista modificada aqui ):
- Vetorização
- Rotinas Cython
- Compreensões de lista (
for
loop de baunilha )
DataFrame.apply()
: i) Reduções que podem ser executadas em cython, ii) Iteração no espaço python
DataFrame.itertuples()
e iteritems()
DataFrame.iterrows()
iterrows
e itertuples
(ambos recebendo muitos votos em respostas a essa pergunta) devem ser usados em circunstâncias muito raras, como a geração de objetos de linha / nametuples para processamento seqüencial, que é realmente a única coisa para a qual essas funções são úteis.
Apelação à autoridade
A página de documentos na iteração tem uma enorme caixa de aviso vermelha que diz:
A iteração através de objetos pandas é geralmente lenta. Em muitos casos, a iteração manual sobre as linhas não é necessária [...].
* Na verdade, é um pouco mais complicado do que "não". df.iterrows()
é a resposta correta para essa pergunta, mas "vetorize suas operações" é a melhor. Admito que há circunstâncias em que a iteração não pode ser evitada (por exemplo, algumas operações em que o resultado depende do valor calculado para a linha anterior). No entanto, é preciso alguma familiaridade com a biblioteca para saber quando. Se você não tem certeza se precisa de uma solução iterativa, provavelmente não precisa. PS: Para saber mais sobre minha justificativa para escrever esta resposta, pule para o final.
Um bom número de operações e cálculos básicos é "vetorizado" pelos pandas (por meio do NumPy ou por funções Cythonized). Isso inclui aritmética, comparações, (a maioria) reduções, remodelagem (como rotação), junções e operações de grupo por grupo. Consulte a documentação sobre Funcionalidade básica essencial para encontrar um método vetorizado adequado para o seu problema.
Se não houver, sinta-se à vontade para criar suas próprias extensões usando o cython personalizado .
A compreensão da lista deve ser o seu próximo porto de escala se 1) não houver uma solução vetorizada disponível, 2) o desempenho for importante, mas não o suficiente para passar pelo incômodo de codificar o código e 3) você estiver tentando executar uma transformação elementar no seu código. Há uma boa quantidade de evidências para sugerir que a compreensão da lista é suficientemente rápida (e às vezes mais rápida) para muitas tarefas comuns dos pandas.
A fórmula é simples,
# iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
Se você pode encapsular sua lógica de negócios em uma função, poderá usar uma compreensão de lista que a chama. Você pode fazer coisas arbitrariamente complexas funcionarem através da simplicidade e velocidade do python bruto.
As
compreensões da Lista de Advertências pressupõem que seus dados sejam fáceis de trabalhar - o que significa que seus tipos de dados são consistentes e você não possui NaNs, mas isso nem sempre pode ser garantido.
- O primeiro é mais óbvio, mas ao lidar com NaNs, prefira métodos de pandas incorporados, se existirem (porque eles têm uma lógica de manipulação de canto muito melhor) ou garanta que sua lógica de negócios inclua uma lógica de manipulação de NaN apropriada.
- Ao lidar com tipos de dados mistos, você deve iterar em
zip(df['A'], df['B'], ...)
vez de, df[['A', 'B']].to_numpy()
pois o último implica implicitamente em transferência de dados para o tipo mais comum. Como exemplo, se A for numérico e B for string, to_numpy()
converterá toda a matriz em string, o que pode não ser o que você deseja. Felizmente, zip
pingar suas colunas juntas é a solução mais direta para isso.
* YMMV pelos motivos descritos na seção Advertências acima.
Um exemplo óbvio
Vamos demonstrar a diferença com um exemplo simples de adição de duas colunas de pandas A + B
. Como é uma operação vetorizável, será fácil contrastar o desempenho dos métodos discutidos acima.
Código de benchmarking, para sua referência.
Devo mencionar, no entanto, que nem sempre é tão fácil assim. Às vezes, a resposta para "qual é o melhor método para uma operação" é "depende dos seus dados". Meu conselho é testar diferentes abordagens nos seus dados antes de escolher um.
Leitura adicional
* Os métodos de string do Pandas são "vetorizados" no sentido em que são especificados na série, mas operam em cada elemento. Os mecanismos subjacentes ainda são iterativos, porque as operações de cadeia de caracteres são inerentemente difíceis de vetorizar.
Por que escrevi esta resposta
Uma tendência comum que noto de novos usuários é fazer perguntas do formulário "como posso iterar meu df para fazer o X?". Mostrando código que chama iterrows()
enquanto faz algo dentro de um loop for. Aqui está o porquê. Um novo usuário da biblioteca que não tenha sido introduzido no conceito de vetorização provavelmente visualizará o código que resolve o problema como iterando sobre os dados para fazer alguma coisa. Sem saber como iterar em um DataFrame, a primeira coisa que eles fazem é pesquisar no Google e acabar aqui, nesta pergunta. Eles veem a resposta aceita dizendo como e fecham os olhos e executam esse código sem antes questionar se a iteração não é a coisa certa a fazer.
O objetivo desta resposta é ajudar os novos usuários a entender que a iteração não é necessariamente a solução para todos os problemas, e que soluções melhores, mais rápidas e mais idiomáticas podem existir, e que vale a pena investir tempo explorando-as. Não estou tentando iniciar uma guerra de iteração x vetorização, mas quero que novos usuários sejam informados ao desenvolver soluções para seus problemas com esta biblioteca.