主要问题:如何以允许我分配给该切片的方式使用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
中的行,其中Color
是Green
,以便它们与传入的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)
...嗯。这样可以得出正确的答案并满足要求,但丑陋的圣牛。
答案 0 :(得分:2)
我先使用assign
和set_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)
解决方案是:
执行此操作的代码是:
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