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)