panda одновременно добавляет несколько новых столбцов на основе значений из других столбцов?

#python #pandas #data-science

#python #python-3.x #pandas

Вопрос:

Как добавить несколько новых столбцов на основе значений из других столбцов одновременно? Я нашел только примеры добавления строки по одному за раз.

Я могу добавить 3 новых столбца, но это не кажется эффективным, поскольку он должен проходить через все строки 3 раза. Есть ли способ обойти DF один раз?

 import pandas as pd
from decimal import Decimal
d = [
    {'A': 2, 'B': Decimal('628.00')},
    {'A': 1, 'B': Decimal('383.00')},
    {'A': 3, 'B': Decimal('651.00')},
    {'A': 2, 'B': Decimal('575.00')},
    {'A': 4, 'B': Decimal('1114.00')},
]

df = pd.DataFrame(d)

In : df
Out:
   A        B
0  2   628.00
1  1   383.00
2  3   651.00
3  2   575.00
4  4  1114.00

# How to do those in one operation to avoid traversing the DF 3 times
df['C'] = df.apply(lambda row: row['B']-1000, axis=1)
df['D'] = df.apply(lambda row: row['B']*row['B'], axis=1)
df['E'] = df.apply(lambda row: row['B']/2, axis=1)

In : df
Out:
   A        B        C             D       E
0  2   628.00  -372.00   394384.0000  314.00
1  1   383.00  -617.00   146689.0000  191.50
2  3   651.00  -349.00   423801.0000  325.50
3  2   575.00  -425.00   330625.0000  287.50
4  4  1114.00   114.00  1240996.0000  557.00
  

Ответ №1:

Я бы не использовал лямбда-функцию. Простая векторизованная реализация быстрее и удобнее для чтения.

 df['C'] = df['B'] - 1000
df['D'] = df['B'] ** 2
df['E'] = df['B'] / 2

>>> df
   A        B        C             D       E
0  2   628.00  -372.00   394384.0000  314.00
1  1   383.00  -617.00   146689.0000  191.50
2  3   651.00  -349.00   423801.0000  325.50
3  2   575.00  -425.00   330625.0000  287.50
4  4  1114.00   114.00  1240996.0000  557.00
  

Давайте рассчитаем время для фрейма данных с миллионом строк:

 df = pd.concat([df for _ in range(200000)], ignore_index=True)
>>> df.shape
(1000000, 2)

>>> %%timeit -n 3
    df['C'] = df.apply(lambda row: row['B'] - 1000, axis=1)
    df['D'] = df.apply(lambda row: row['B'] * row['B'], axis=1)
    df['E'] = df.apply(lambda row: row['B'] / 2, axis=1)
3 loops, best of 3: 1min 20s per loop

>>> %%timeit -n 3
    df['C'] = df['B'] - 1000
    df['D'] = df['B'] ** 2
    df['E'] = df['B'] / 2
3 loops, best of 3: 49.7 s per loop
  

Скорость значительно выше, если вы отказались от десятичного типа и вместо этого использовали значение с плавающей точкой:

 d = [
    {'A': 2, 'B': 628.00},
    {'A': 1, 'B': 383.00},
    {'A': 3, 'B': 651.00},
    {'A': 2, 'B': 575.00},
    {'A': 4, 'B': 1114.00}]

df = pd.DataFrame(d)
df = pd.concat([df for _ in range(200000)], ignore_index=True)

>>> %%timeit -n 3
    df['C'] = df['B'] - 1000
    df['D'] = df['B'] ** 2
    df['E'] = df['B'] / 2
3 loops, best of 3: 33.1 ms per loop

>>> df.shape
(1000000, 5)
  

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

1. Важна ли скорость этой операции, неизвестно — вероятно, это совсем не так. Тем не менее, это, безусловно, хорошая, нормальная, читаемая вещь — использовать последовательные операции, а не применять в подобных случаях.

2. @MikeGraham Учитывая комментарий OP во втором абзаце об эффективности, я интерпретирую это как эффективность для скорости.

3. Отличный ответ. Спасибо. Если я хорошо понимаю, как работает pandas, ему придется проходить через весь DF каждый раз для каждой строки / нового столбца, который мы добавляем правильно? Таким образом, в этом случае он будет проходить 3 раза по всему DF, потому что мы добавляем 3 столбца. Если я хочу добавить больше столбцов, есть ли способ улучшить это, поскольку нет смысла каждый раз просматривать весь DF. Возможно ли вычислить все новые столбцы в каждой строке одновременно и, следовательно, выполнить цикл через DF только один раз? Может быть, это все равно не быстрее? Я еще не очень хорошо знаю pandas. Спасибо.

4. Кроме того, разница с использованием float впечатляет. df['B'] = df['B'].astype(numpy.float64) Был бы правильным способом приведения столбца к десятичным объектам? Спасибо.

5. Да, вы можете использовать как df['B'].astype('float') или df['B'].astype(np.float) , или df['B'].astype(np.float32)