Чому функція "Pandas" застосовувати ", що посилається на кілька стовпців, не працює? [зачинено]


239

У мене є деякі проблеми із функцією застосування Pandas, коли використовуються декілька стовпців із наступним фреймом даних

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

та наступна функція

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

Коли я намагаюся застосувати цю функцію за допомогою:

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

Я отримую повідомлення про помилку:

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

Я не розумію цього повідомлення, ім'я я правильно визначив.

Я дуже вдячний за будь-яку допомогу з цього питання

Оновлення

Спасибі за вашу допомогу. Я дійсно зробив деякі синтаксичні помилки з кодом, індекс слід поставити ''. Однак я все одно отримую ту саму проблему, використовуючи більш складну функцію, таку як:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 

1
Уникайте використання applyякомога більше. Якщо ви не впевнені, що вам потрібно користуватися ним, ви, ймовірно, цього не зробите. Рекомендую поглянути на те, Коли я коли-небудь хочу використовувати панди, що застосовують () у своєму коді? .
cs95

Йдеться лише про синтаксичні помилки, що посилаються на стовпець фрейму даних, і чому функціям потрібні аргументи. Що стосується вашого другого запитання, функція my_test(a)не знає, що dfтаке, оскільки вона не була передана як аргумент (якщо тільки dfце не має бути глобальним, що було б жахливо). Вам потрібно передати всі значення, які вам знадобляться у функції, як аргументи (бажано в порядку), інакше як би інша функція знала, звідки dfпоходить? Крім того, погана практика програмувати в просторі імен, усіяних глобальними змінними, ви не будете вловлювати подібні помилки.
smci

Відповіді:


379

Здається, ви забули ''свій рядок.

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, на мою думку, наступний спосіб є більш елегантним:

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

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

Дякую, ти маєш рацію, я забув ''. Однак у мене все одно те саме питання з більш складною функцією. Я дуже вдячний за вашу допомогу в цьому. Спасибі
Енді

5
@Andy наступні [53-54] дозволяють застосовувати більш складні функції.
Енді Хайден

@Andy ви можете визначити свою складну функцію, як In [53].
waitkuo

чи всі стратегії застосування застосовуються однаково? Я новачок в пандах і завжди знаходив застосування трохи загадкової, але вашу стратегію в [53-54] мені легко зрозуміти (і, сподіваюся, пам’ятаю) ... на великому столі це так швидко, як і інша форма подання заявки представлений?
whytheq

Чому саме створення окремого методу вважається більш елегантним - навіть для крихітних методів. Я займаюся значними проектами в python протягом 7 років, але, ймовірно, ніколи не вважатимуться pythonistaчерез певні перспективи, включаючи цю.
javadba

33

Якщо ви просто хочете обчислити (стовпець a)% (стовпець b), вам не потрібно apply, просто зробіть це безпосередньо:

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

16
Я знаю, це лише приклад, щоб показати мою проблему в застосуванні функції до кількох стовпців
Енді

18

Скажімо, ми хочемо застосувати функцію add5 до стовпців 'a' та 'b' DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)

Під час спроби вашого фрагмента коду я отримую наступну помилку. TypeError: ("має бути str, не int", "сталося в індексі b") Ви можете, будь ласка, розглянути це.
debaonline4u

Стовпчик b вашого фрейму даних - це рядок типу рядка або тип об'єкта, він повинен бути цілим стовпцем, який слід додати з числом.
Mir_Murtaza

Чи зміни не застосовуватимуться лише після призначення?
S.aad

11

Всі запропоновані вище пропозиції працюють, але якщо ви хочете, щоб ваші обчислення були більш ефективними, вам слід скористатися операціями з нумерованими векторами (як зазначено тут) .

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)})

Приклад 1: циклічне використання за допомогою pandas.apply():

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

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

Самий повільний біг зайняв у 7,49 рази довший, ніж найшвидший. Це може означати, що проміжний результат кешується. 1000 петель, найкраще 3: 481 мкс на цикл

Приклад 2: векторизація з використанням pandas.apply():

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

Самий повільний біг зайняв у 458,85 рази довший, ніж найшвидший. Це може означати, що проміжний результат кешується. 10000 петель, найкраще 3: 70,9 мкс на цикл

Приклад 3: векторизація за допомогою numpy масивів:

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

Самий повільний біг зайняв у 7,98 рази довше, ніж найшвидший. Це може означати, що проміжний результат кешується. 100000 петель, найкраще 3: 6,39 мкс на цикл

Таким чином, векторизація за допомогою нумерованих масивів покращила швидкість майже на два порядки.


Результати змінюються ще більш різко для великих чисел, наприклад, замінюючи 6 на 10 К, я отримую відповідно 248 мс, 332 мкс, 263 мкс. Так обидва векторизовані рішення набагато ближче один до одного, але невекторизований розчин у 1000 разів повільніше. (випробуваний на python-3.7)
стасон

3

Це те саме, що і в попередньому рішенні, але я визначив функцію в df.apply:

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

2

Я дав порівняння всіх трьох обговорених вище.

Використання значень

% timeit df ['value'] = df ['a']. Значення% df ['c'].

139 мкс ± 1,91 мкс на цикл (середнє ± ст. Розряд 7 пробігів, 10000 циклів у кожній)

Без значень

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

216 мкс ± 1,86 мкс на цикл (середнє ± ст. Розряд 7 пробігів, 1000 циклів у кожній)

Застосувати функцію

% timeit df ['Значення'] = df.apply (рядок лямбда: рядок ['a']% рядок ['c'], вісь = 1)

474 мкс ± 5,07 мкс на цикл (середнє ± ст. Розряд 7 пробігів, 1000 циклів у кожній)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.