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