Pandas умножить числовые кадры с несколькими индексами и перекрывающимися уровнями индексов
Я борюсь с задачей, которая должна быть простой, но она не работает, как я думал. У меня есть два числовых dataframes A и B с несколькими индексами и столбцами ниже:
A = A B C D
X 1 AX1 BX1 CX1 DX1
2 AX2 BX2 CX2 DX2
3 AX3 BX3 CX3 DX3
Y 1 AY1 BY1 CY1 DY1
2 AY2 BY2 CY2 DY2
3 AY3 BY3 CY3 DY3
B = A B C D
X 1 a AX1a BX1a CX1a DX1a
b AX1b BX1b CX1b DX1b
c AX1c BX1c CX1c DX1c
2 a AX2a BX2a CX2a DX2a
b AX2b BX2b CX2b DX2b
c AX2c BX2c CX2c DX2c
3 a AX3a BX3a CX3a DX3a
b AX3b BX3b CX3b DX3b
c AX3c BX3c CX3c DX3c
Y 1 a AY1a BY1a CY1a DY1a
b AY1b BY1b CY1b DY1b
c AY1c BY1c CY1c DY1c
2 a AY2a BY2a CY2a DY2a
b AY2b BY2b CY2b DY2b
c AY2c BY2c CY2c DY2c
3 a AY3a BY3a CY3a DY3a
b AY3b BY3b CY3b DY3b
c AY3c BY3c CY3c DY3c ## Heading ##
Я хотел бы размножать A * B, транслируя по самому внутреннему уровню B, я хочу получить результирующий фрейм R ниже:
R= A B C D
X 1 a (AX1a * AX1) (BX1a * BX1) (CX1a * CX1) (DX1a * DX1)
b (AX1b * AX1) (BX1b * BX1) (CX1b * CX1) (DX1b * DX1)
c (AX1c * AX1) (BX1c * BX1) (CX1c * CX1) (DX1c * DX1)
2 a (AX2a * AX2) (BX2a * BX2) (CX2a * CX2) (DX2a * DX2)
b (AX2b * AX2) (BX2b * BX2) (CX2b * CX2) (DX2b * DX2)
c (AX2c * AX2) (BX2c * BX2) (CX2c * CX2) (DX2c * DX2)
3 a (AX3a * AX3) (BX3a * BX3) (CX3a * CX3) (DX3a * DX3)
b (AX3b * AX3) (BX3b * BX3) (CX3b * CX3) (DX3b * DX3)
c (AX3c * AX3) (BX3c * BX3) (CX3c * CX3) (DX3c * DX3)
Y 1 a (AY1a * AY1) (BY1a * BY1) (CY1a * CY1) (DY1a * DY1)
b (AY1b * AY1) (BY1b * BY1) (CY1b * CY1) (DY1b * DY1)
c (AY1c * AY1) (BY1c * BY1) (CY1c * CY1) (DY1c * DY1)
2 a (AY2a * AY2) (BY2a * BY2) (CY2a * CY2) (DY2a * DY2)
b (AY2b * AY2) (BY2b * BY2) (CY2b * CY2) (DY2b * DY2)
c (AY2c * AY2) (BY2c * BY2) (CY2c * CY2) (DY2c * DY2)
3 a (AY3a * AY3) (BY3a * BY3) (CY3a * CY3) (DY3a * DY3)
b (AY3b * AY3) (BY3b * BY3) (CY3b * CY3) (DY3b * DY3)
c (AY3c * AY3) (BY3c * BY3) (CY3c * CY3) (DY3c * DY3)
Я попытался использовать функцию pandas умножить с ключевым словом level, выполнив:
b.multiply(a, level=[0,1])
но он выдает ошибку: "TypeError: объединение на уровне между двумя объектами MultiIndex неоднозначно"
Каков правильный способ выполнения этой операции?
Ответы
Ответ 1
Я просто использовал DF.reindex
в меньшей форме DF
, чтобы соответствовать индексу более крупного DF's
формы и форварда заполнить значения, присутствующие в нем. Затем сделайте умножение.
B.multiply(A.reindex(B.index, method='ffill')) # Or method='pad'
Demo:
Подготовить некоторые данные:
np.random.seed(42)
midx1 = pd.MultiIndex.from_product([['X', 'Y'], [1,2,3]])
midx2 = pd.MultiIndex.from_product([['X', 'Y'], [1,2,3], ['a','b','c']])
A = pd.DataFrame(np.random.randint(0,2,(6,4)), midx1, list('ABCD'))
B = pd.DataFrame(np.random.randint(2,4,(18,4)), midx2, list('ABCD'))
Маленький DF
:
>>> A
A B C D
X 1 0 1 0 0
2 0 1 0 0
3 0 1 0 0
Y 1 0 0 1 0
2 1 1 1 0
3 1 0 1 1
Большой DF
:
>>> B
A B C D
X 1 a 3 3 3 3
b 3 3 2 2
c 3 3 3 2
2 a 3 2 2 2
b 2 2 3 3
c 3 3 3 2
3 a 3 3 2 3
b 2 3 2 3
c 3 2 2 2
Y 1 a 2 2 2 2
b 2 3 3 2
c 3 3 3 3
2 a 2 3 2 3
b 3 3 2 3
c 2 3 2 3
3 a 2 2 3 2
b 3 3 3 3
c 3 3 3 3
Умножая их, убедившись, что они имеют общую ось индекса на всех уровнях:
>>> B.multiply(A.reindex(B.index, method='ffill'))
A B C D
X 1 a 0 3 0 0
b 0 3 0 0
c 0 3 0 0
2 a 0 2 0 0
b 0 2 0 0
c 0 3 0 0
3 a 0 3 0 0
b 0 3 0 0
c 0 2 0 0
Y 1 a 0 0 2 0
b 0 0 3 0
c 0 0 3 0
2 a 2 3 2 0
b 3 3 2 0
c 2 3 2 0
3 a 2 0 3 2
b 3 0 3 3
c 3 0 3 3
Теперь вы можете даже поставить параметр level
в DF.multiply
для того, чтобы трансляция имела место в этих соответствующих индексах.
Ответ 2
Предлагаемый подход
Мы говорим о broadcasting
, поэтому я хотел бы включить здесь NumPy supported broadcasting
.
Код решения будет выглядеть примерно так:
def numpy_broadcasting(df0, df1):
m,n,r = map(len,df1.index.levels)
a0 = df0.values.reshape(m,n,-1)
a1 = df1.values.reshape(m,n,r,-1)
out = (a1*a0[...,None,:]).reshape(-1,a1.shape[-1])
df_out = pd.DataFrame(out, index=df1.index, columns=df1.columns)
return df_out
Основная идея:
1] Получить представления в dataframe как многомерные массивы. Многомерность поддерживается в соответствии с структурой уровня мультииндекса. Таким образом, первый блок данных будет иметь три уровня (включая столбцы), а второй - четыре уровня. Таким образом, мы имеем a0
и a1
, соответствующие входным кадрам данных df0
и df1
, в результате получим a0
и a1
с размерами 3
и 4
.
2) Теперь идет вещательная часть. Мы просто расширяем a0
, чтобы иметь 4 измерения, введя новую ось в третью позицию. Эта новая ось будет соответствовать третьей оси от df1
. Это позволяет нам выполнять умножение по элементам.
3) Наконец, чтобы получить выходной мультидакс данных, мы просто преобразуем продукт.
Пример прогона:
1) Входные данные -
In [369]: df0
Out[369]:
A B C D
0 0 3 2 2 3
1 6 8 1 0
2 3 5 1 5
1 0 7 0 3 1
1 7 0 4 6
2 2 0 5 0
In [370]: df1
Out[370]:
A B C D
0 0 0 4 6 1 2
1 3 3 4 5
2 8 1 7 4
1 0 7 2 5 4
1 8 6 7 5
2 0 4 7 1
2 0 1 4 2 2
1 2 3 8 1
2 0 0 5 7
1 0 0 8 6 1 7
1 0 6 1 4
2 5 4 7 4
1 0 4 7 0 1
1 4 2 6 8
2 3 1 0 6
2 0 8 4 7 4
1 0 6 2 0
2 7 8 6 1
2) Выходной кадр -
In [371]: df_out
Out[371]:
A B C D
0 0 0 12 12 2 6
1 9 6 8 15
2 24 2 14 12
1 0 42 16 5 0
1 48 48 7 0
2 0 32 7 0
2 0 3 20 2 10
1 6 15 8 5
2 0 0 5 35
1 0 0 56 0 3 7
1 0 0 3 4
2 35 0 21 4
1 0 28 0 0 6
1 28 0 24 48
2 21 0 0 36
2 0 16 0 35 0
1 0 0 10 0
2 14 0 30 0
Бенчмаркинг
In [31]: # Setup input dataframes of the same shape as stated in the question
...: individuals = list(range(2))
...: time = (0, 1, 2)
...: index = pd.MultiIndex.from_tuples(list(product(individuals, time)))
...: A = pd.DataFrame(data={'A': np.random.randint(0,9,6), \
...: 'B': np.random.randint(0,9,6), \
...: 'C': np.random.randint(0,9,6), \
...: 'D': np.random.randint(0,9,6)
...: }, index=index)
...:
...:
...: individuals = list(range(2))
...: time = (0, 1, 2)
...: P = (0,1,2)
...: index = pd.MultiIndex.from_tuples(list(product(individuals, time, P)))
...: B = pd.DataFrame(data={'A': np.random.randint(0,9,18), \
...: 'B': np.random.randint(0,9,18), \
...: 'C': np.random.randint(0,9,18), \
...: 'D': np.random.randint(0,9,18)}, index=index)
...:
# @DSM solution
In [32]: %timeit B * A.loc[B.index.droplevel(2)].set_index(B.index)
1 loops, best of 3: 8.75 ms per loop
# @Nickil Maveli solution
In [33]: %timeit B.multiply(A.reindex(B.index, method='ffill'))
1000 loops, best of 3: 625 µs per loop
# @root solution
In [34]: %timeit B * np.repeat(A.values, 3, axis=0)
1000 loops, best of 3: 487 µs per loop
In [35]: %timeit numpy_broadcasting(A, B)
1000 loops, best of 3: 191 µs per loop
Ответ 3
Обратите внимание, что я не утверждаю, что это правильный способ сделать эту операцию, только чтобы это было одним из способов сделать это. У меня были проблемы с правильной моделью вещания в прошлом.: -/
Короткий вариант заключается в том, что я завершаю трансляцию вручную и создаю соответствующий промежуточный объект:
In [145]: R = B * A.loc[B.index.droplevel(2)].set_index(B.index)
In [146]: A.loc[("X", 2), "C"]
Out[146]: 0.5294149302910357
In [147]: A.loc[("X", 2), "C"] * B.loc[("X", 2, "c"), "C"]
Out[147]: 0.054262618238601339
In [148]: R.loc[("X", 2, "c"), "C"]
Out[148]: 0.054262618238601339
Это работает путем индексирования в с использованием соответствующих частей B, а затем для установки индекса. Если бы я был более умным, я мог бы найти собственный способ заставить это работать, но я еще не знаю.: - (