Pandas: создайте два новых столбца в фрейме данных со значениями, вычисленными из уже существующего столбца
Я работаю с панды библиотека и я хочу добавить два новых столбца в фрейм данных df
С n столбцами (n > 0).
Эти новые столбцы являются результатом применения функции к одному из столбцов в фрейме данных.
функция для применения выглядит так:
def calculate(x):
...operate...
return z, y
один из способов создания нового столбца для функции, возвращающей только значение:
df['new_col']) = df['column_A'].map(a_function)
Итак, то, что я хочу, и пробовал безуспешно ( * ), это что-то например:
(df['new_col_zetas'], df['new_col_ys']) = df['column_A'].map(calculate)
какой лучший способ сделать это может быть ? Я просмотрел документация без понятия.
**df['column_A'].map(calculate)
возвращает серию панд каждый элемент, состоящий из кортежа z, y. и пытается присвоить это двум столбцам dataframe производит ValueError.*
2 ответа:
Я бы просто использовать
zip
:In [1]: from pandas import * In [2]: def calculate(x): ...: return x*2, x*3 ...: In [3]: df = DataFrame({'a': [1,2,3], 'b': [2,3,4]}) In [4]: df Out[4]: a b 0 1 2 1 2 3 2 3 4 In [5]: df["A1"], df["A2"] = zip(*df["a"].map(calculate)) In [6]: df Out[6]: a b A1 A2 0 1 2 2 3 1 2 3 4 6 2 3 4 6 9
верхний ответ, на мой взгляд, ошибочен. Надеюсь, никто не массово импортирует всех панд в свое пространство имен с
from pandas import *
. Кроме того,map
метод должен быть зарезервирован для тех случаев, когда он передает словарь или серию. Он может взять функцию, но это то, чтоapply
используется для.Итак, если вы должны использовать вышеуказанный подход, я бы написал это так
df["A1"], df["A2"] = zip(*df["a"].apply(calculate))
на самом деле нет причин использовать zip здесь. Вы можете просто сделать это:
df["A1"], df["A2"] = calculate(df['a'])
этот второй метод также намного быстрее на больших кадрах данных
df = pd.DataFrame({'a': [1,2,3] * 100000, 'b': [2,3,4] * 100000})
DataFrame создан с 300 000 строк
%timeit df["A1"], df["A2"] = calculate(df['a']) 2.65 ms ± 92.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) %timeit df["A1"], df["A2"] = zip(*df["a"].apply(calculate)) 159 ms ± 5.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
60x быстрее, чем zip
в общем, избегайте использования apply
применить, как правило, не намного быстрее, чем итерации по списку Python. Давайте проверим производительность for-loop, чтобы сделать то же самое, что и выше
%%timeit A1, A2 = [], [] for val in df['a']: A1.append(val**2) A2.append(val**3) df['A1'] = A1 df['A2'] = A2 298 ms ± 7.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
так что это в два раза медленнее, что не является ужасная регрессия производительности, но если мы цитируем вышеизложенное, мы получаем гораздо лучшую производительность. Предполагая, что вы используете ipython:
%load_ext cython %%cython cpdef power(vals): A1, A2 = [], [] cdef double val for val in vals: A1.append(val**2) A2.append(val**3) return A1, A2 %timeit df['A1'], df['A2'] = power(df['a']) 72.7 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
прямое назначение без применения
вы можете получить еще большие улучшения скорости, если вы используете прямые векторизованные операции.
%timeit df['A1'], df['A2'] = df['a'] ** 2, df['a'] ** 3 5.13 ms ± 320 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
это использует чрезвычайно быстрые векторизованные операции NumPy вместо наших циклов. Теперь у нас есть 30-кратное ускорение по оригинальный.
самый простой тест скорости с
apply
приведенный выше пример должен наглядно показать, как медленно
apply
может быть, но просто так его дополнительные ясно давайте посмотрим на самый простой пример. Давайте квадрат серии из 10 миллионов чисел с и без примененияs = pd.Series(np.random.rand(10000000)) %timeit s.apply(calc) 3.3 s ± 57.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
без применения в 50 раз быстрее
%timeit s ** 2 66 ms ± 2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)