Умножьте столбцы в Dataframe, где столбцы - pd.MultiIndex - PullRequest
3 голосов
/ 02 августа 2020

Я хочу умножить 2 столбца (A * B) в DataFrame, где столбцы - это pd.MultiIndex. Я хочу выполнить это умножение для каждого столбца DataX (Data1, Data2, ...) в столбцах level = 0.

df = pd.DataFrame(data= np.arange(32).reshape(8,4), 
                  columns = pd.MultiIndex.from_product(iterables = [["Data1","Data2"],["A","B"]]))

    Data1   Data2
    A   B   A   B
0   0   1   2   3
1   4   5   6   7
2   8   9   10  11
3   12  13  14  15
4   16  17  18  19
5   20  21  22  23
6   24  25  26  27
7   28  29  30  31

Результатом умножения также должен быть DataFrame с columns = pd.MultiIndex ( см. ниже).

    Data1   Data2   Data1   Data2
    A   B   A   B   A*B     A*B
0   0   1   2   3   0       6
1   4   5   6   7   20      42
2   8   9   10  11  72      110
3   12  13  14  15  156     210
4   16  17  18  19  272     342
5   20  21  22  23  420     506
6   24  25  26  27  600     702
7   28  29  30  31  812     930

Мне удалось выполнить это умножение, перебирая столбцы, level = 0, но ищу лучший способ сделать это.

for _ in df.columns.get_level_values(level=0).unique().tolist()[:]:
    df[(_, "A*B")] = df[(_, "A")] * df[(_, "B")]

Любые предложения или подсказки очень признателен! Спасибо

Ответы [ 4 ]

4 голосов
/ 02 августа 2020

Вырежьте буквы «A» и «B» на первом уровне столбца Index. Затем вы можете умножить, который будет выровнен по 0-му уровню («Данные1», «Данные2»). Затем мы воссоздадим MultiIndex в столбцах и снова подключим

df1 = df.xs('A', axis=1, level=1).multiply(df.xs('B', axis=1, level=1))
df1.columns = pd.MultiIndex.from_product([df1.columns, ['A*B']])

df = pd.concat([df, df1], axis=1)

Вот некоторые тайминги, если у вас есть 2 группы (Data1, Data2) и ваш DataFrame становится длиннее. Оказывается, простой l oop может быть самым быстрым из всех. (Я добавил некоторую сортировку, и мне нужно было скопировать их все, чтобы результат был одинаковым).

import perfplot
import pandas as pd
import numpy as np

#@Tom
def simple_loop(df):
    for _ in df.columns.get_level_values(level=0).unique().tolist()[:]:
        df[(_, "A*B")] = df[(_, "A")] * df[(_, "B")]
    return df.sort_index(axis=1)

#@Roy2012
def mul_with_stack(df):
    df = df.stack(level=0)
    df["A*B"] = df.A * df.B
    return df.stack().swaplevel().unstack(level=[2,1]).sort_index(axis=1)

#@Alollz
def xs_concat(df):
    df1 = df.xs('A', axis=1, level=1).multiply(df.xs('B', axis=1, level=1))
    df1.columns = pd.MultiIndex.from_product([df1.columns, ['A*B']])

    return pd.concat([df, df1], axis=1).sort_index(axis=1)

#@anky
def prod_join(df):
    u = df.prod(axis=1,level=0)
    u.columns=pd.MultiIndex.from_product((u.columns,['*'.join(df.columns.levels[1])]))
    return df.join(u).sort_index(axis=1)


perfplot.show(
    setup=lambda n: pd.DataFrame(data=np.arange(4*n).reshape(n, 4), 
                                 columns =pd.MultiIndex.from_product(iterables=[["Data1", "Data2"], ["A", "B"]])), 
    kernels=[
        lambda df: simple_loop(df.copy()),
        lambda df: mul_with_stack(df.copy()),
        lambda df: xs_concat(df.copy()),
        lambda df: prod_join(df.copy())
    ],
    labels=['simple_loop', 'stack_and_multiply', 'xs_concat', 'prod_join'],
    n_range=[2 ** k for k in range(3, 20)],
    equality_check=np.allclose, 
    xlabel="len(df)"
)

введите описание изображения здесь

4 голосов
/ 02 августа 2020

Вот еще один вариант с использованием df.prod и df.join

u = df.prod(axis=1,level=0)
u.columns=pd.MultiIndex.from_product((u.columns,['*'.join(df.columns.levels[1])]))
out = df.join(u)
  Data1     Data2     Data1 Data2
      A   B     A   B   A*B   A*B
0     0   1     2   3     0     6
1     4   5     6   7    20    42
2     8   9    10  11    72   110
3    12  13    14  15   156   210
4    16  17    18  19   272   342
5    20  21    22  23   420   506
6    24  25    26  27   600   702
7    28  29    30  31   812   930
2 голосов
/ 02 августа 2020

Вот способ сделать это с помощью stack и unstack. Преимущество: полностью векторизованный, без циклов, без операций соединения.

t = df.stack(level=0)
t["A*B"] = t.A * t.B
t = t.stack().swaplevel().unstack(level=[2,1])

Результат:

  Data1          Data2         
      A   B  A*B     A   B  A*B
0     0   1    0     2   3    6
1     4   5   20     6   7   42
2     8   9   72    10  11  110
3    12  13  156    14  15  210
4    16  17  272    18  19  342
1 голос
/ 03 августа 2020

Другая альтернатива здесь, используя prod :

df[("Data1", "A*B")] = df.loc(axis=1)["Data1"].prod(axis=1)
df[("Data2", "A*B")] = df.loc(axis=1)["Data2"].prod(axis=1)

df

   Data1    Data2 Data1 Data2
   A    B   A   B  A*B  A*B
0   0   1   2   3   0   6
1   4   5   6   7   20  42
2   8   9   10  11  72  110
3   12  13  14  15  156 210
4   16  17  18  19  272 342
5   20  21  22  23  420 506
6   24  25  26  27  600 702
7   28  29  30  31  812 930
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...