Как добавить новую строку в фрейм данных MultiIndex на основе двух или более условий на основе фрейма данных - PullRequest
2 голосов
/ 09 мая 2019

Я пытаюсь добавить новую строку в мой фрейм данных MultiIndex на основе более чем одного условия.

Следующий код показывает, как я устанавливаю исходный фрейм данных, с которым я хочу работать (не реальный, нодостаточно хорошо, чтобы проиллюстрировать проблему, с которой я сталкиваюсь ...):

def mklbl(prefix, n):
    return ["%s%s" % (prefix, i) for i in range(n)]

----


    miindex = pd.MultiIndex.from_product([mklbl('A', 3),
                                           mklbl('B', 4),
                                           ], names=['A','B'])

    micolumns = pd.MultiIndex.from_tuples([('a', 'foo'), ('a', 'bar'),
                                           ('b', 'foo'), ('b', 'bah')],
                                           names=['lvl0', 'lvl1'])


    df = pd.DataFrame(np.random.randint(100, size=len(miindex) * len(micolumns))
                        .reshape((len(miindex), len(micolumns))),
                         index=miindex,
                         columns=micolumns).sort_index().sort_index(axis=1)                         

    df = df.drop([('A2','B2'),('A2','B3')])

Вот начало df:

lvl0    a       b    
lvl1  bar foo bah foo
A  B                 
A0 B0  38  16   1  31
   B1  49  12  97  73
   B2  76  26  17  34
   B3   8  55   6  80
A1 B0  40  94  57   0
   B1  21  23  45  78
   B2  36  31  89  18
   B3  46  31  57  94
A2 B0  46  98  62  97
   B1  89  26  93  95

Вопрос: как мне создатьновый индекс такой, что в каждом A, который имеет B2 и B3 записей, если B3 < B2, то B4 = B3 / B2 else B4 = 0;

Я ожидаю, что результат будет таким:


lvl0    a       b    
lvl1  bar foo bah foo
A  B                 
A0 B0  38       16   1          31
   B1  49       12  97          73
   B2  76       26  17          34
   B3   8       55   6          80
   B4  0.105     0  0.352        0
A1 B0  40       94  57           0
   B1  21       23  45          78
   B2  36       31  89          18
   B3  46       31  57          94
   B4   0        0  0.640        0
A2 B0  46       98  62          97
   B1  89       26  93          95

Это очень трудно для меня сделать.Я пытался сделать это самостоятельно в течение всего дня, используя pd.IndexSlice, get_level_values, boolean indexer, а что нет, но безрезультатно.Это так расстраивает.

Спасибо за любую помощь.

Ответы [ 2 ]

0 голосов
/ 09 мая 2019

groupby(level=...) может помочь в этом, разделив фрейм данных в соответствии с уровнем A мультииндекса.Оттуда достаточно найти, существуют ли оба значения B2 и B3, и если да, вычислить новую строку.

Необходимо будет выполнить сортировку по индексу, чтобы поместить новые строки вих правильное место.Код может быть:

for A, g in df.index.to_frame().groupby(level='A'):
    if ((A, 'B2') in g.index) and ((A, 'B3') in g.index):
        df.loc[(A, 'B4'), :] = np.where(
            df.loc[(A, 'B3')]<df.loc[(A, 'B2')],
            df.loc[(A, 'B3')] / df.loc[(A, 'B2')],
            0)

df.sort_index(inplace=True)
0 голосов
/ 09 мая 2019

Одним из способов может быть преобразование данных, чтобы вы могли выполнять эти операции со столбцами, а не со строками:

df = df.T.stack('A')

Затем вы можете использовать numpy.where в соответствии с вашими условиями:

df['B4'] = np.where(df.B3 < df.B2, df.B3 / df.B2, 0)

В предыдущей строке будут сохранены значения 0, где B2 или B3 равно nan.Чтобы это исправить:

df['B4'] = np.where(df.B3.isnull() | df.B2.isnull(), np.nan, df.B4)

Теперь вы можете преобразовать обратно:

df = df.stack('B').unstack(['lvl0', 'lvl1'])

В одном наборе:

df = df.T.stack('A')
df['B4'] = np.where(df.B3 < df.B2, df.B3 / df.B2, 0)
df['B4'] = np.where(df.B3.isnull() | df.B2.isnull(), np.nan, df.B4)
df = df.stack('B').unstack(['lvl0', 'lvl1'])

со следующимфрейм данных:

lvl0    a       b
lvl1  bar foo bah foo
A  B
A0 B0  59  41  50  82
   B1  77   7  15  77
   B2  91  57  44  98
   B3  74  54  52  88
A1 B0  35  47  68   4
   B1  93  93  20  93
   B2  68  76  67  29
   B3   9  50  25   3
A2 B0   7  59  69  74
   B1  19  34  40  32

Это дает:

lvl0           a                     b
lvl1         bar        foo        bah        foo
A  B
A0 B0  59.000000  41.000000  50.000000  82.000000
   B1  77.000000   7.000000  15.000000  77.000000
   B2  91.000000  57.000000  44.000000  98.000000
   B3  74.000000  54.000000  52.000000  88.000000
   B4   0.813187   0.947368   0.000000   0.897959
A1 B0  35.000000  47.000000  68.000000   4.000000
   B1  93.000000  93.000000  20.000000  93.000000
   B2  68.000000  76.000000  67.000000  29.000000
   B3   9.000000  50.000000  25.000000   3.000000
   B4   0.132353   0.657895   0.373134   0.103448
A2 B0   7.000000  59.000000  69.000000  74.000000
   B1  19.000000  34.000000  40.000000  32.000000
...