Как применить функцию к объекту pandas groupby и сохранить результаты обратно в новый столбец родительского фрейма данных? - PullRequest
3 голосов
/ 16 апреля 2019

У меня есть кадр данных pandas, который выглядит следующим образом:

In [5]: import pandas as pd                                                     

In [6]: df = pd.DataFrame({'X': [0, 123, 342, 353, 467, 345, 789, 543, 3913], 
   ...:                    'Y': [0, 12, 23, 41, 23, 45, 23, 53, 23], 
   ...:                    'Group': [0, 1, 2, 0, 1, 2, 0, 1, 2]})               

In [7]: df                                                                      
Out[7]: 
      X   Y  Group
0     0   0      0
1   123  12      1
2   342  23      2
3   353  41      0
4   467  23      1
5   345  45      2
6   789  23      0
7   543  53      1
8  3913  23      2

Три группы представляют серию измерений, и я хотел бы вычислить евклидово расстояние до предыдущего элемента для каждого измерения серии и добавитьэто за измерение.(расстояние первого измерения = 0).

Я прочитал все темы форума о том, как переназначить результат операции groupby обратно на родительский фрейм данных.Но в моем случае вычисления чего-то для каждой записи в кадре данных (не агрегирования) на основе групп я не мог найти никакого решения.

Поэтому я хочу знать, как объединить эти шаги:

from scipy.spatial.distance import euclidean

# 1. Group data
group = df.groupby('Group')
# 2. Calculate cumulative euclidean distance for each group
group['Distance'] = group.apply(lambda row: euclidean(row['X'], row['Y']).cumsum(), axis=1)
# 3. Assign back to original dataframe

Шаг 1 довольно прост.На втором этапе я попробовал множество комбинаций df.groupby.apply и df.groupby.apply.transform, а также определил свои собственные функции (не знаю, будет ли это соответствовать одному вкладышу).Но я не мог заставить его вести себя так, как я хочу.Я предполагаю, что groupby().transform() - это то, что я хочу, но я не могу заставить его работать построчно.

Также для переназначения результата моему исходному кадру данных вместо одного объекта groupby я попытался df.join, pd.merge, pd.concat и так далее, но сейчас я нахожусь в точке, где я нахожусьочень запутано, какая разница: D.

Мой желаемый результат будет:

Out[7]: 
      X   Y  Group  Distance  Cumulative Distance
0     0   0      0         0                    0
1   123  12      1         0                    0 
2   342  23      2         0                    0
3   353  41      0    355.37               355.37   
4   467  23      1    344.17               344.17     
5   345  45      2     22.20                22.20    
6   789  23      0    436.37               791.74     
7   543  53      1     81.71               425.88     
8  3913  23      2   3568.07              3590.44 

Мне нужно только суммарное расстояние (снова вычисляется для группы).Но я перечислил индивидуальное расстояние как промежуточный шаг.

1 Ответ

1 голос
/ 16 апреля 2019

Используйте groupby apply с shift, чтобы получить каждую строку предыдущей точки, затем используйте bfill, чтобы заполнить первую точку самой собой.

После этого создайте новые столбцы с zip X и Y вместе.

df.sort_values('Group', inplace=True)
df[['X_shift', 'Y_shift']] = df.groupby('Group')[['X', 'Y']].apply(lambda x: x.shift(1)).bfill()
df['point_1'] = tuple(zip(df.X, df.Y))
df['point_2'] = tuple(zip(df.X_shift, df.Y_shift))

df

      X   Y  Group  X_shift  Y_shift     point_1        point_2
0     0   0      0      0.0      0.0      (0, 0)     (0.0, 0.0)
3   353  41      0      0.0      0.0   (353, 41)     (0.0, 0.0)
6   789  23      0    353.0     41.0   (789, 23)  (353.0, 41.0)
1   123  12      1    123.0     12.0   (123, 12)  (123.0, 12.0)
4   467  23      1    123.0     12.0   (467, 23)  (123.0, 12.0)
7   543  53      1    467.0     23.0   (543, 53)  (467.0, 23.0)
2   342  23      2    342.0     23.0   (342, 23)  (342.0, 23.0)
5   345  45      2    342.0     23.0   (345, 45)  (342.0, 23.0)
8  3913  23      2    345.0     45.0  (3913, 23)  (345.0, 45.0)

И используйте apply для вычисления евклидова расстояния в каждой точке, затем используйте groupby с cumsum для получения окончательного результата.

df['Distance'] = df.apply(lambda row: euclidean(row.point_1, row.point_2), axis=1)

df

      X   Y  Group  X_shift  Y_shift     point_1        point_2     Distance
0     0   0      0      0.0      0.0      (0, 0)     (0.0, 0.0)     0.000000
3   353  41      0      0.0      0.0   (353, 41)     (0.0, 0.0)   355.373043
6   789  23      0    353.0     41.0   (789, 23)  (353.0, 41.0)   436.371401
1   123  12      1    123.0     12.0   (123, 12)  (123.0, 12.0)     0.000000
4   467  23      1    123.0     12.0   (467, 23)  (123.0, 12.0)   344.175827
7   543  53      1    467.0     23.0   (543, 53)  (467.0, 23.0)    81.706793
2   342  23      2    342.0     23.0   (342, 23)  (342.0, 23.0)     0.000000
5   345  45      2    342.0     23.0   (345, 45)  (342.0, 23.0)    22.203603
8  3913  23      2    345.0     45.0  (3913, 23)  (345.0, 45.0)  3568.067824

df['Cumulative Distance'] = df.groupby('Group').Distance.cumsum()

# Drop unuse columns
df.drop(columns=['X_shift', 'Y_shift', 'point_1', 'point_2'], inplace=True)
df.sort_index(inplace=True)
df

      X   Y  Group     Distance  Cumulative Distance
0     0   0      0     0.000000             0.000000
1   123  12      1     0.000000             0.000000
2   342  23      2     0.000000             0.000000
3   353  41      0   355.373043           355.373043
4   467  23      1   344.175827           344.175827
5   345  45      2    22.203603            22.203603
6   789  23      0   436.371401           791.744445
7   543  53      1    81.706793           425.882620
8  3913  23      2  3568.067824          3590.271428
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...