#python #dataframe #multi-index
Вопрос:
У меня есть многоиндексный фрейм данных Pandas на Python с двумя уровнями для индекса и для столбцов, которые выглядят следующим образом:
miind = pd.MultiIndex.from_product([['A1','A2'],['B1','B2','B3']]) micol = pd.MultiIndex.from_product([['X1','X2'],['Y1','Y2','Y3']]) df = pd.DataFrame((np.arange(len(miind)*len(micol)) % 5).reshape(len(miind),len(micol)), index=miind, columns=micol) print(df)
X1 X2 Y1 Y2 Y3 Y1 Y2 Y3 A1 B1 0 1 2 3 4 0 B2 1 2 3 4 0 1 B3 2 3 4 0 1 2 A2 B1 3 4 0 1 2 3 B2 4 0 1 2 3 4 B3 0 1 2 3 4 0
Я хотел бы для каждой строки разделить Y1 и Y2 на Y3, когда Y3 не равно 0. Я не знаю, как совместить условие Y3gt;0 с выбором элемента.
Каков наилучший способ сделать это? np.where()
, маска или просто индексирование? Я получаю доступ к Y3 следующим образом:
idx = pd.IndexSlice print(df.loc[idx[:,:],idx[:,'Y3']] gt; 0)
X1 X2 Y3 Y3 A1 B1 True False B2 True True B3 True True A2 B1 False True B2 True True B3 True False
Редактировать:
Это то, что я хочу, используя for
циклы:
A = ['A1','A2'] B = ['B1','B2','B3'] X = ['X1','X2'] Y = ['Y1','Y2','Y3'] miind = pd.MultiIndex.from_product([A,B]) micol = pd.MultiIndex.from_product([X,Y]) df = pd.DataFrame((np.arange(len(miind)*len(micol)) % 5).reshape(len(miind),len(micol)), index=miind, columns=micol) for i, a in enumerate(A): df1 = df.loc[a] for j,b in enumerate(B): df2 = df1.loc[b] for k,x in enumerate(X): s1 = df2.loc[x] if s1['Y3'] gt; 0: df.loc[idx[a,b],idx[x,'Y1']] /= s1['Y3'] df.loc[idx[a,b],idx[x,'Y2']] /= s1['Y3'] print(df)
X1 X2 Y1 Y2 Y3 Y1 Y2 Y3 A1 B1 0.000000 0.500000 2 3.000000 4.000000 0 B2 0.333333 0.666667 3 4.000000 0.000000 1 B3 0.500000 0.750000 4 0.000000 0.500000 2 A2 B1 3.000000 4.000000 0 0.333333 0.666667 3 B2 4.000000 0.000000 1 0.500000 0.750000 4 B3 0.000000 0.500000 2 3.000000 4.000000 0
Однако это решение не является элегантным и, вероятно, плохо масштабируется для больших кадров данных…
Ответ №1:
Вы можете складывать и распаковывать свой фрейм данных:
# stack the dataframe tmp = df.stack(level=0) # divide the columns of the stacked dataframe tmp.loc[tmp['Y3']!= 0, 'Y1'] /= tmp.loc[tmp['Y3']!= 0, 'Y3'] tmp.loc[tmp['Y3']!= 0, 'Y2'] /= tmp.loc[tmp['Y3']!= 0, 'Y3'] # unstack the divided dataframe tmp = tmp.unstack(level=2)
На данный момент у нас есть:
Y1 Y2 Y3 X1 X2 X1 X2 X1 X2 A1 B1 0.000000 3.000000 0.500000 4.000000 2 0 B2 0.333333 4.000000 0.666667 0.000000 3 1 B3 0.500000 0.000000 0.750000 0.500000 4 2 A2 B1 3.000000 0.333333 4.000000 0.666667 0 3 B2 4.000000 0.500000 0.000000 0.750000 1 4 B3 0.000000 3.000000 0.500000 4.000000 2 0
Не так уж плохо, что уровни столбцов-это не то, что мы хотим. Давайте продолжим…
# reverse the column levels tmp.columns = pd.MultiIndex.from_tuples((j,i) for i,j in tmp.columns) # and sort the columns result = tmp.sort_index(axis=1)
Теперь у нас есть, как и ожидалось:
X1 X2 Y1 Y2 Y3 Y1 Y2 Y3 A1 B1 0.000000 0.500000 2 3.000000 4.000000 0 B2 0.333333 0.666667 3 4.000000 0.000000 1 B3 0.500000 0.750000 4 0.000000 0.500000 2 A2 B1 3.000000 4.000000 0 0.333333 0.666667 3 B2 4.000000 0.000000 1 0.500000 0.750000 4 B3 0.000000 0.500000 2 3.000000 4.000000 0