我有一个以下形式的多级数据框:
df = pd.DataFrame([[1,'A',2,'B',1,'B'],[2,'B',2,'B',2,'A'],[1,'A',1,'A',1,'A'],[1,'B',2,'A',2,'B']],
columns=pd.MultiIndex.from_tuples([('S1','Num'),('S1','Let'),('S2','Num'),('S2','Let'),('S3','Num'),('S3','Let')]))
S1 S2 S3
Num Let Num Let Num Let
0 1 A 2 B 1 B
1 2 B 2 B 2 A
2 1 A 1 A 1 A
3 1 B 2 A 2 B
如何创建一个新的数据框,以便选择每个级别的零列,如果设置为=='B',那么它设置Num = 3?基本上我想得到以下数据帧:
S1 S2 S3
Num Let Num Let Num Let
0 1 A 3 B 3 B
1 3 B 3 B 2 A
2 1 A 1 A 1 A
3 3 B 2 A 3 B
答案 0 :(得分:4)
一种方法是按位置工作并使用iloc
和where
:
>>> df.iloc[:,0::2] = df.iloc[:,0::2].where((df.iloc[:,1::2]!="B").values, 3)
>>> df
S1 S2 S3
Num Let Num Let Num Let
0 1 A 3 B 3 B
1 3 B 3 B 2 A
2 1 A 1 A 1 A
3 3 B 2 A 3 B
这使用数组
>>> (df.iloc[:,1::2]!="B").values
array([[ True, False, False],
[False, False, True],
[ True, True, True],
[False, True, False]], dtype=bool)
决定我们需要单独保留原始值的位置。我们必须坚持.values
,因为否则pandas会尝试对齐框架,我们会手动完成。
如果你坚持不使用iloc,那么事情变得有点棘手。一种方法是使用xs
和update
:
>>> df.update(df.xs("Num", level=1, axis=1, drop_level=False).where(
df.xs("Let", level=1, axis=1, drop_level=False).values != "B", 3))
>>> df
S1 S2 S3
Num Let Num Let Num Let
0 1 A 3 B 3 B
1 3 B 3 B 2 A
2 1 A 1 A 1 A
3 3 B 2 A 3 B
或者如果您的列是lexsorted,则可以将loc
与slice(None)
一起使用:
>>> df = df.sort_index(axis=1)
>>> nummask = slice(None), "Num"
>>> letmask = slice(None), "Let"
>>> df.loc[:, nummask] = df.loc[:, nummask].where((df.loc[:, letmask] != "B").values, 3)
>>> df
S1 S2 S3
Let Num Let Num Let Num
0 A 1 B 3 B 3
1 B 3 B 3 A 2
2 A 1 A 1 A 1
3 B 3 A 2 B 3
使用IndexSlice可以稍微简化一下:
>>> df = df.sort_index(axis=1)
>>> idx = pd.IndexSlice
>>> df.loc[:,idx[:,"Num"]] = df.loc[:,idx[:,"Num"]].where((df.loc[:,idx[:,"Let"]] != "B").values, 3)
>>> df
S1 S2 S3
Let Num Let Num Let Num
0 A 1 B 3 B 3
1 B 3 B 3 A 2
2 A 1 A 1 A 1
3 B 3 A 2 B 3