pandas применяет функцию к нескольким столбцам и нескольким строкам

#python #pandas

#python #pandas

Вопрос:

У меня есть фрейм данных с последовательными координатами пикселей в строках и столбцах «xpos», «ypos», и я хочу рассчитать угол в градусах каждого пути между последовательными пикселями. В настоящее время у меня есть решение, представленное ниже, которое отлично работает и для размера моего файла достаточно быстрое, но итерация по всем строкам, похоже, не является способом pandas сделать это. Я знаю, как применять функцию к разным столбцам и как применять функции к разным строкам столбцов, но не могу понять, как объединить оба.

вот мой код:

 fix_df = pd.read_csv('fixations_out.csv')

# wyliczanie kąta sakady
temp_list=[]
for count, row in df.iterrows():
    x1 = row['xpos']
    y1 = row['ypos']
    try:
        x2 = df['xpos'].ix[count-1]
        y2 = df['ypos'].ix[count-1]
        a = abs(180/math.pi * math.atan((y2-y1)/(x2-x1)))
        temp_list.append(a)
    except KeyError:
        temp_list.append(np.nan)
  

а затем я вставляю временный список в df

РЕДАКТИРОВАТЬ: после реализации подсказки из комментария у меня есть:

 df['diff_x'] = df['xpos'].shift() - df['xpos']
df['diff_y'] = df['ypos'].shift() - df['ypos']

def calc_angle(x):
    try:
        a = abs(180/math.pi * math.atan((x.diff_y)/(x.diff_x)))
        return a
    except ZeroDivisionError:
        return 0

df['angle_degrees'] = df.apply(calc_angle, axis=1)
  

Я сравнил время трех решений для моего df (размер df составляет около 6 тыс. строк), итерация почти в 9 раз медленнее, чем apply, и примерно в 1500 раз медленнее, чем выполнение без apply:

время выполнения решения с итерацией, включая вставку нового столбца обратно в df: 1,51 с

время выполнения решения без итерации с применением: 0,17 с

время выполнения принятого ответа EdChum с использованием diff(), без итерации и без применения: 0,001 с

Предложение: не используйте итерацию или apply и всегда старайтесь использовать векторное вычисление 😉 это не только быстрее, но и более читабельно.

Комментарии:

1. Для начала вы можете вычислить разницу, df['xpos'].shift() - df['xpos'] вместо того чтобы делать это по строкам, затем вы можете вычислить угол, используя свою функцию для всего столбца

2. Я обновил свой ответ, я получаю производительность менее 1 мс, что на много порядков быстрее

Ответ №1:

Вы можете сделать это с помощью следующего метода, и я сравнил способ pandas с вашим способом, и это более чем в 1000 раз быстрее, и это без добавления списка обратно в виде нового столбца! Это было сделано для фрейма данных с 10000 строками

 In [108]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].shift() - df['xpos']/df['ypos'].shift() - df['ypos']))

1000 loops, best of 3: 1.27 ms per loop

In [100]:

%%timeit
temp_list=[]
for count, row in df.iterrows():
    x1 = row['xpos']
    y1 = row['ypos']
    try:
        x2 = df['xpos'].ix[count-1]
        y2 = df['ypos'].ix[count-1]
        a = abs(180/math.pi * math.atan((y2-y1)/(x2-x1)))
        temp_list.append(a)
    except KeyError:
        temp_list.append(np.nan)
1 loops, best of 3: 1.29 s per loop
  

Также, если возможно, избегайте использования apply , поскольку это работает по строкам, если вы можете найти векторизованный метод, который может работать со всей серией или фреймом данных, тогда всегда предпочитайте это.

Обновить

поскольку вы просто выполняете вычитание из предыдущей строки, для этого есть встроенный метод diff , что приводит к еще более быстрому кодированию:

 In [117]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].diff(1)/df['ypos'].diff(1)))

1000 loops, best of 3: 1.01 ms per loop
  

Еще одно обновление

Существует также встроенный метод для разделения рядов и фреймов данных, теперь это экономит больше времени, и я достигаю времени менее 1 мс:

 In [9]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].diff(1).div(df['ypos'].diff(1))))

1000 loops, best of 3: 951 µs per loop
  

Комментарии:

1. abs должно быть np.abs в первом случае?

2. @joris, да для согласованности, но это мало что изменило в 1,27 мс против 1,29 мс, я обновлю ответ, спасибо