Как векторизовать соединение по строкам

#python #pandas #dataframe #vectorization

Вопрос:

Мне дали df фрейм данных и функцию foo(int x) , которая возвращает серию. Я хотел бы присоединиться df к результату foo() для каждой строки, используя векторизованную операцию.

Например, учитывая следующий фрейм данных, я вызываю foo() значения столбца col_1

col_1 col_2 col_3
1 1 «а»
12 2 «б»
13 3 «д»
4 4 «с»

Если мы предположим, что

  • foo(1) = Series('col4': 0, 'col5': 2)
  • foo(12) = Series('col4': 1, 'col5': 3)
  • foo(13) = Series('col4': 1, 'col5': 4)
  • foo(4) = Series('col4': 0, 'col5': 5)

тогда результат должен быть

col_1 col_2 col_3 col4 col5
1 1 «а» 0 2
12 2 «б» 1 3
13 3 «д» 1 4
4 4 «с» 0 5

Ответ №1:

ПРАВКА: Похоже .from_records , что карта будет обработана чисто. Вы могли бы попробовать это с pd.concat помощью вместо:

 In [118]: pd.DataFrame.from_records(df['col_1'].map(foo))
Out[118]:
   col4  col5
0     0     2
1     1     3
2     1     4
3     0     5
 

Обычно я бы использовал .map() для чего-то подобного, так как это обычно быстрее , чем .apply() , но вывод получается немного пугающим, поэтому, если у вас нет гигантского фрейма данных, я бы просто использовал простой .apply() вариант с pd.concat :

 In [18]: def foo(n):
    ...:     return {1: pd.Series({'col4': 0, 'col5': 2}), 12: pd.Series({'col4': 1, 'col5': 3}), 13: pd.Series({'col4': 1, 'col5': 4}), 4: pd.Series
    ...: ({'col4': 0, 'col5': 5})}[n]
    ...:

In [19]: df
Out[19]:
   col_1  col_2 col_3
0      1      1   'a'
1     12      2   'b'
2     13      3   'd'
3      4      4   'c'

In [20]: pd.concat([df, df['col_1'].apply(foo)], axis=1)
Out[20]:
   col_1  col_2 col_3  col4  col5
0      1      1   'a'     0     2
1     12      2   'b'     1     3
2     13      3   'd'     1     4
3      4      4   'c'     0     5
 

Другой вариант, который вы можете попробовать, — это чтобы функция возвращала словарь, а не серию

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

1. Как бы я использовал .map() ? .apply() кажется, на это уходит целая вечность

2. Я не знаю, в чем заключается ваш фактический расчет, foo но проще всего было бы разделить его на отдельные функции для col4 col5 вычислений и, а затем выполнить df['col4'] = df['col1'].map(foo_c4) , а затем аналогично для col5 . Это может быть немного медленнее, если вы, по сути, вычисляете оба значения в одном и том же вычислении.

3. Да, расчет на самом деле не может быть разбит и foo фактически возвращает 1126 строк, так что это не совсем выполнимо. Я пытался использовать map раньше, и это была проблема, которую я не мог понять 🙂

4. Похоже pd.DataFrame.from_records , очищает странный .map вывод. Я отредактировал ответ, чтобы отразить это, так что вы можете попробовать вместо .apply этого .

Ответ №2:

Поскольку ваша функция не может принимать ничего, кроме скаляра, вы ограничены в использовании Series.apply . На самом деле, это почти так, как если бы ваша функция была создана именно для этого случая использования

Поскольку, если функция возвращает объект серии, конечным результатом будет фрейм данных, который легко присоединить к исходному. Отсюда вы используете pd.concat вдоль правильной оси

 pd.concat([df, df.iloc[:,0].apply(foo)], axis=1)