在通过MultiIndex名称选择时如何分配给Pandas DataFrame?

时间:2020-01-31 20:18:15

标签: python pandas

主要问题:如何以允许我分配给该切片的方式使用MultiIndex级别的名称选择/切片一个多索引的DataFrame?

测试数据

data = io.StringIO('''Fruit,Color,Count,Price
Apple,Red,3,$1.29
Apple,Green,9,$0.99
Pear,Red,25,$2.59
Pear,Green,26,$2.79
Lime,Green,9999,$0.39
''')
df_fruit = pd.read_csv(data, index_col=['Fruit', 'Color'])

new_green_data = io.StringIO('''Fruit,Count,Price
Apple,2,$0.96
Lime,9993,$0.40
Pear,12,$2.90
''')
df_new_green = pd.read_csv(new_green_data, index_col='Fruit')

这将设置两个DataFrame:

df_fruit

| Fruit   | Color   |   Count | Price   |
|:--------|:--------|--------:|:--------|
| Apple   | Red     |       3 | $1.29   |
| Apple   | Green   |       9 | $0.99   |
| Pear    | Red     |      25 | $2.59   |
| Pear    | Green   |      26 | $2.79   |
| Lime    | Green   |    9999 | $0.39   |

df_new_green

| Fruit   |   Count | Price   |
|:--------|--------:|:--------|
| Apple   |       2 | $0.96   |
| Lime    |    9993 | $0.40   |
| Pear    |      12 | $2.90   |

旺旺

我想更新df_fruit中的行,其中ColorGreen,以便它们与传入的df_new_green数据中的值匹配。最终输出应为:

| Fruit   | Color   |   Count | Price   |
|:--------|:--------|--------:|:--------|
| Apple   | Red     |       3 | $1.29   |
| Apple   | Green   |       2 | $0.96   |
| Pear    | Red     |      25 | $2.59   |
| Pear    | Green   |      12 | $2.90   |
| Lime    | Green   |    9993 | $0.40   |

请注意,df_new_green中水果的顺序不同于df_fruit。因此,在执行分配时,我需要保留双方的索引,以便正确处理。

我所知道的

我知道几种在DataFrame中选择要更新的内容的方法:

df_fruit.xs(key='Green', level='Color')

这将产生正确的数据视图,但我无法为其分配数据。同样关闭:

df_fruit[df_fruit.index.get_level_values('Color') == 'Green']

idx = pd.IndexSlice
df_fruit.loc[idx[:, 'Green'], :]

两者都给了我相同的看法,但是它们仍然包括MultiIndex的Color级别:

| Fruit   | Color   |   Count | Price   |
|:--------|:--------|--------:|:--------|
| Apple   | Green   |       9 | $0.99   |
| Pear    | Green   |      26 | $2.79   |
| Lime    | Green   |    9999 | $0.39   |

我可以使用df_new_green分配给该视图,但这会产生NaN,因为df_new_green的索引中不包含Color级别。第二个选择(使用IndexSlice)也不是很好,因为我不是根据级别的名称来选择级别,而是根据其在MultiIndex中的位置来选择。如果我在任何一个上运行droplevel('Green'),我都将获得正确的视图,但无法将其分配给它。

我可以在新值上删除索引,但这会导致使用了错误的值:

df_fruit.loc[idx[:, 'Green'], :] = df_new_green._values

这将产生:

| Fruit   | Color   |   Count | Price   |
|:--------|:--------|--------:|:--------|
| Apple   | Red     |       3 | $1.29   |
| Apple   | Green   |       2 | $0.96   |
| Pear    | Red     |      25 | $2.59   |
| Pear    | Green   |    9993 | $0.40   |
| Lime    | Green   |      12 | $2.90   |

...但是这是错误的,因为交换了Pear和Lime值。我需要在更新DataFrame上保留索引。

丑陋的方式

df_fruit[df_fruit.index.get_level_values('Color') == 'Green'] = df_new_green.assign(Color='Green').set_index('Color', append=True)

...嗯。这样可以得出正确的答案并满足要求,但丑陋的圣牛。

3 个答案:

答案 0 :(得分:2)

我先使用assignset_index然后使用combine_first

(df_new_green.assign(Color='Green')
             .set_index('Color', append=True)
             .combine_first(df_fruit))

输出:

|    | Fruit   | Color   |   Count | Price   |
|---:|:--------|:--------|--------:|:--------|
|  0 | Apple   | Green   |       2 | $0.96   |
|  1 | Apple   | Red     |       3 | $1.29   |
|  2 | Lime    | Green   |    9993 | $0.40   |
|  3 | Pear    | Green   |      12 | $2.90   |
|  4 | Pear    | Red     |      25 | $2.59   |

答案 1 :(得分:1)

解决方案是:

  • 添加 Green 作为 df_new_green 中索引的第二级, 将其名称设置为 Color
  • 使用此(临时)DataFrame(就地)更新 df_fruit

执行此操作的代码是:

df_fruit.update(df_new_green.set_index(pd.Index(
    ['Green'] * df_new_green.index.size, name='Color'), append=True))

答案 2 :(得分:0)

不太好,但是它能做到。

new_prices = []
for index, row in df_fruit.iterrows():
    if index[1] == 'Green':
        price = df_new_green.loc[index[0], ['Price']].values[0]
        new_prices.append(price)
    else:
        new_prices.append(row['Price'])

df_fruit['Price'] = new_prices

输出:

             Count  Price
Fruit Color              
Apple Red        3  $1.29
      Green      9  $0.96
Pear  Red       25  $2.59
      Green     26  $2.90
Lime  Green   9999  $0.40
相关问题