Por que a função 'aplicar' do meu Panda, que faz referência a várias colunas, não está funcionando? [fechadas]

239

Tenho alguns problemas com a função de aplicação do Pandas, ao usar várias colunas com o seguinte quadro de dados

df = DataFrame ({'a' : np.random.randn(6),
                 'b' : ['foo', 'bar'] * 3,
                 'c' : np.random.randn(6)})

e a seguinte função

def my_test(a, b):
    return a % b

Quando tento aplicar esta função com:

df['Value'] = df.apply(lambda row: my_test(row[a], row[c]), axis=1)

Eu recebo a mensagem de erro:

NameError: ("global name 'a' is not defined", u'occurred at index 0')

Não entendi esta mensagem, defini o nome corretamente.

Eu apreciaria muito qualquer ajuda sobre esta questão

Atualizar

Obrigado pela ajuda. Cometi alguns erros de sintaxe com o código, o índice deve ser colocado ''. No entanto, ainda recebo o mesmo problema usando uma função mais complexa, como:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 
Andy
fonte
1
Evite usar applyo máximo possível. Se você não tem certeza de que precisa usá-lo, provavelmente não precisa. Eu recomendo dar uma olhada em Quando devo usar pandas apply () no meu código? .
cs95
Trata-se de erros de sintaxe que referenciam uma coluna de quadro de dados e por que as funções precisam de argumentos. Quanto à sua segunda pergunta, a função my_test(a)não sabe o que dfé, pois não foi passada como argumento (a menos que dfseja suposto ser global, o que seria uma prática terrível). Você precisa passar todos os valores necessários dentro de uma função como argumentos (de preferência em ordem); caso contrário, de que outra forma a função saberia de onde dfvem? Além disso, é uma prática ruim programar em um espaço para nome repleto de variáveis ​​globais; você não detectará erros como esse.
SMCI

Respostas:

379

Parece que você esqueceu o ''seu fio.

In [43]: df['Value'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)

In [44]: df
Out[44]:
                    a    b         c     Value
          0 -1.674308  foo  0.343801  0.044698
          1 -2.163236  bar -2.046438 -0.116798
          2 -0.199115  foo -0.458050 -0.199115
          3  0.918646  bar -0.007185 -0.001006
          4  1.336830  foo  0.534292  0.268245
          5  0.976844  bar -0.773630 -0.570417

BTW, na minha opinião, da seguinte maneira é mais elegante:

In [53]: def my_test2(row):
....:     return row['a'] % row['c']
....:     

In [54]: df['Value'] = df.apply(my_test2, axis=1)
waitingkuo
fonte
Obrigado, você está certo, eu esqueci o ''. No entanto, ainda tenho o mesmo problema com uma função mais complexa. Eu apreciaria muito sua ajuda com isso. Obrigado
Andy
5
@Andy [53-54] permite aplicar funções mais complexas.
Andy Hayden
@ Andy, você pode definir sua função complexa como a maneira [53].
waitingkuo
todas as estratégias de aplicação têm o mesmo desempenho? Eu sou novo nos pandas e sempre achei a aplicação um pouco enigmática, mas sua estratégia em [53-54] é fácil para eu entender (e espero lembrar) ... em uma mesa grande, é tão rápido quanto a outra forma de aplicação apresentado?
whytheq
Por que a criação de um método separado é considerada mais elegante - mesmo para métodos minúsculos. Eu tenho feito projetos significativos em python por 7 anos, mas provavelmente nunca será considerado pythonistadevido a algumas perspectivas, incluindo esta.
Javadba # 20/18
33

Se você deseja apenas calcular (coluna a)% (coluna b), não precisa apply, basta fazê-lo diretamente:

In [7]: df['a'] % df['c']                                                                                                                                                        
Out[7]: 
0   -1.132022                                                                                                                                                                    
1   -0.939493                                                                                                                                                                    
2    0.201931                                                                                                                                                                    
3    0.511374                                                                                                                                                                    
4   -0.694647                                                                                                                                                                    
5   -0.023486                                                                                                                                                                    
Name: a
herrfz
fonte
16
Eu sei, é apenas um exemplo para mostrar o meu problema na aplicação de uma função para várias colunas
Andy
18

Digamos que queremos aplicar uma função add5 às colunas 'a' e 'b' do DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)
Mir_Murtaza
fonte
Estou recebendo o seguinte erro ao tentar seu snippet de código. TypeError: ('deve ser str, não int', 'ocorreu no índice b'), você pode verificar isso.
Debaonline4u 08/08/19
A coluna b do seu quadro de dados é um tipo de cadeia ou coluna de objeto, deve ser uma coluna inteira a ser adicionada com um número.
Mir_Murtaza 08/08/19
As alterações não se aplicariam apenas após a atribuição?
S.aad
11

Todas as sugestões acima funcionam, mas se você quiser que seus cálculos sejam mais eficientes, aproveite as operações de vetores numpy (como indicado aqui) .

import pandas as pd
import numpy as np


df = pd.DataFrame ({'a' : np.random.randn(6),
             'b' : ['foo', 'bar'] * 3,
             'c' : np.random.randn(6)})

Exemplo 1: loop com pandas.apply():

%%timeit
def my_test2(row):
    return row['a'] % row['c']

df['Value'] = df.apply(my_test2, axis=1)

A corrida mais lenta levou 7,49 vezes mais que a mais rápida. Isso pode significar que um resultado intermediário está sendo armazenado em cache. 1000 loops, o melhor de 3: 481 µs por loop

Exemplo 2: vetorizar usando pandas.apply():

%%timeit
df['a'] % df['c']

A corrida mais lenta levou 458,85 vezes mais que a mais rápida. Isso pode significar que um resultado intermediário está sendo armazenado em cache. 10000 loops, o melhor de 3: 70,9 µs por loop

Exemplo 3: vetorizar usando matrizes numpy:

%%timeit
df['a'].values % df['c'].values

A corrida mais lenta levou 7,98 vezes mais que a mais rápida. Isso pode significar que um resultado intermediário está sendo armazenado em cache. 100000 loops, o melhor de 3: 6,39 µs por loop

Portanto, a vetorização usando matrizes numpy melhorou a velocidade em quase duas ordens de magnitude.

Blane
fonte
Os resultados mudam ainda mais drasticamente para grandes números, por exemplo, substituindo 6 por 10K, recebo 248 ms, 332 µs, 263 µs, respectivamente. Portanto, ambas as soluções vetorizadas estão muito mais próximas uma da outra, mas a solução não vetorizada é 1000 vezes mais lenta. (testado em python-3.7)
stason em
3

Isso é igual à solução anterior, mas eu defini a função no próprio df.apply:

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
shaurya airi
fonte
2

Eu dei a comparação dos três discutidos acima.

Usando valores

% timeit df ['value'] = df ['a']. values% df ['c']. values

139 µs ± 1,91 µs por loop (média ± desvio padrão de 7 execuções, 10000 loops cada)

Sem valores

% timeit df ['value'] = df ['a']% df ['c'] 

216 µs ± 1,86 µs por loop (média ± desvio padrão de 7 execuções, 1000 loops cada)

Aplicar função

% timeit df ['Value'] = df.apply (linha lambda: linha ['a']% linha ['c'], eixo = 1)

474 µs ± 5,07 µs por loop (média ± desvio padrão de 7 execuções, 1000 loops cada)

Gursewak Singh
fonte