排序后的多索引后的python groupby不正确
尊敬的python/pandas专家: 我在对多索引数据帧的索引进行排序时遇到问题。更具体地说,排序似乎有效,但之后的groupby操作再次“忘记”了排序。供参考:我正在运行python 3.4.3(由anaconda编写)和pandas 0.16.2(np19py34_0) 现在详细说明我要做的事情 我创建了一个multindex数据框,如下所示排序后的多索引后的python groupby不正确,python,sorting,pandas,Python,Sorting,Pandas,尊敬的python/pandas专家: 我在对多索引数据帧的索引进行排序时遇到问题。更具体地说,排序似乎有效,但之后的groupby操作再次“忘记”了排序。供参考:我正在运行python 3.4.3(由anaconda编写)和pandas 0.16.2(np19py34_0) 现在详细说明我要做的事情 我创建了一个multindex数据框,如下所示 import pandas as pd label="sdjks" sidechar="B" mi_level_fields = (label, s
import pandas as pd
label="sdjks"
sidechar="B"
mi_level_fields = (label, sidechar)
mi_level_names = ["Label", "Side"]
pipeinfo_index = pd.MultiIndex.from_tuples([mi_level_fields], names=mi_level_names)
pipeinfoDF = pd.DataFrame(index=pipeinfo_index, columns=[])
pipeinfoDF.ix[(label, sidechar), "Nc"] = 10
pipeinfoDF.ix[(label, "C"), "Nc"] = 10
pipeinfoDF.ix[("ztest", "C"), "Nc"] = 400
pipeinfoDF.ix[("ztest", "B"), "Nc"] = 400
pipeinfoDF.ix[("yaki", "B"), "Nc"] = 1
pipeinfoDF.ix[("yaki", "C"), "Nc"] = 1
这个pipeinfoDF数据帧现在看起来像
Nc
Label Side
sdjks B 10
C 10
ztest C 400
B 400
yaki B 1
C 1
现在我想对数据帧的索引进行排序,以使NC列按升序排列。这可以通过
pipeinfoDF.sort_index(by=["Nc"], inplace=True, ascending=True)
这确实正确地产生了使用
print(pipeinfoDF.head())
Nc
Label Side
yaki B 1
C 1
sdjks B 10
C 10
ztest C 400
B 400
然而,当我想要循环这个多索引数据帧的行时,问题就出现了,我通常使用它
for (label, df) in pipeinfoDF.groupby(level=0, sort=False):
side_list = df.index.get_level_values('Side')
for side in side_list:
data = pipeinfoDF.ix[(label, side)]
print(label, side, data.Nc)
现在作为一个输出
sdjks B 10.0
sdjks C 10.0
ztest C 400.0
ztest B 400.0
yaki B 1.0
yaki C 1.0
正如您所看到的,尽管head()语句显示数据帧已正确排序,但在索引上循环(我通常这样做是为了将数据复制到另一个表)时,似乎没有使用正确的排序索引
在我看来,这似乎是一个bug:groupby语句中的sort选项对结果没有影响,而且也有类似的报道
现在我的问题是:有没有简单的方法来解决这个问题?head语句似乎正确地给出了我的排序后的multindex数据帧,所以我一直在尝试复制这个head语句的输出
result = pipeinfoDF.head()
但这似乎不起作用
我最后一次尝试尝试根据重置的索引创建新的数据帧:
tmp = pipeinfoDF.copy()
tmp.reset_index(inplace=True)
lbls = tmp.Label.values
sds = tmp.Side.values
pipeinfo_index2 = pd.MultiIndex.from_tuples(list(zip(lbls,sds)), names=mi_level_names)
pipeinfoDF2 = pd.DataFrame(index=pipeinfo_index2, columns=[])
for index, row in tmp.iterrows():
for col in tmp.columns[2:]:
pipeinfoDF2.ix[(row["Label"], row["Side"]), col] = row[col]
再次使用head()我得到了正确的结果
Nc
Label Side
yaki B 1
C 1
sdjks B 10
C 10
ztest C 400
但是,对于前面的multindex帧上的循环,它再次对第一个组进行排序,我使用sort=False显式地阻止了这一点
for (label, df) in pipeinfoDF2.groupby(level=0, sort=False):
side_list = df.index.get_level_values('Side')
for side in side_list:
data = pipeinfoDF2.ix[(label, side)]
print(label, side, data.Nc)
这给
sdjks B 10.0
sdjks C 10.0
yaki B 1.0
yaki C 1.0
ztest C 400.0
ztest B 400.0
因此groupby选项似乎再次按照第一个索引排序
编辑:我发现以下内容可以修复此问题。如果打印数据框的索引,则其标签不按数字顺序排列:
print(pipeinfoDF2.index)
MultiIndex(levels=[['sdjks', 'yaki', 'ztest'], ['B', 'C']],
labels=[[1, 1, 0, 0, 2, 2], [0, 1, 0, 1, 1, 0]],
names=['Label', 'Side'])
这里的级别为“sdjks”、“yaki”、“ztest”,标签对应顺序为1,1,0,0,2,2
绘制第一个排序的pipeinfoDF的索引时也可以看到同样的情况,其中sort_索引保持多索引中级别的顺序,但只更改标签的顺序
因此,我可以通过强制标签以0,0,1,1,2,2的形式运行来解决问题,因为groupby显然会忽略标签的顺序,并且总是选择级别的顺序。因此,我的解决办法是
pipeinfo_index2 = pd.MultiIndex.from_tuples([tuples[0]], names=mi_level_names)
pipeinfoDF2 = pd.DataFrame(index=pipeinfo_index2, columns=[])
然后像以前一样填充其余的字段。通过这种方式,multindex看起来像
MultiIndex(levels=[['yaki', 'sdjks', 'ztest'], ['B', 'C']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 1, 0]],
names=['Label', 'Side'])
现在使用groupby在行上循环给出以下输出
yaki B 1.0
yaki C 1.0
sdjks B 10.0
sdjks C 10.0
ztest C 400.0
ztest B 400.0
这是正确的
因此,我发现了一个非常糟糕的解决方法:在排序之后,将整个数据帧复制到一个新的数据帧,重置索引,然后将所有内容复制回,以强制多索引标签按数字顺序排列。但我认为这是非常低效的,并且会生成大量代码,我相信这是可能做到更高效的
因此,我的问题是:有没有一种方法可以在考虑标签顺序的多索引数据帧的行上循环?这显然被忽视了。我错过什么了吗?希望有一个更简单的方法来做到这一点
任何提示,谢谢
编辑:
Firelynx的建议有效。如果我这样做
for (label,side) in pipeinfoDF.index:
data = pipeinfoDF.ix[(label, side)]
print(label, side, data.Nc)
在第一次排序之后,我正确地按排序顺序获取数据
yaki B 1.0
yaki C 1.0
sdjks B 10.0
sdjks C 10.0
ztest C 400.0
ztest B 400.0
节省了我大量的编码。然而,剩下的问题是:如果groupby上的sort=False选项不能产生相同的结果吗?这是一个bug,还是我错过了使用groupby方法在数据帧上循环。它基于我在谷歌上搜索过的例子,但使用时应该小心。
无论如何,现在我解决了我的问题,我打算通过访问数据的方式放弃我的groupby
编辑:
Firelynx的解决方案是可行的,但是,它不再考虑多级结构,而是将所有标签和边级别放在一个列表中
为了得到与我想要使用groupby方法非常相似的东西,我现在做以下的hack
label_list = []
for (label,side) in pipeinfoDF.index:
if not label in label_list:
label_list.append(label)
for label in label_list:
df = pipeinfoDF.loc[label]
side_list = df.index.get_level_values('Side')
for side in side_list:
data = pipeinfoDF.ix[(label, side)]
print(label, side, data.Nc)
正确的结果是
yaki B 1.0
yaki C 1.0
sdjks B 10.0
sdjks C 10.0
ztest C 400.0
ztest B 400.0
因此,我首先使用Firelynx的建议提取已排序的标签列表,然后循环遍历该列表以获得每个标签的边,并对其执行我想执行的操作。虽然这比我的第一个方法要干净得多,但我仍然觉得它可以更直接地完成。我无法想象您不能在排序的多索引数据帧上使用groupby方法而不扰乱排序顺序。也许有人有个建议?无论如何,现在我对这个解决方案很满意
基于Firelynx的最新建议,我有一个小的更新,使它更干净一点。不过,您仍然需要保留一个列表,以防止标签重复计数,因为unique仅适用于unique(标签,侧面)组合。所以我现在有
label_list = []
for (label, side) in pipeinfoDF.index.unique():
if not label in label_list:
label_list.append(label)
else:
continue
df = pipeinfoDF.loc[label]
side_list = df.index.get_level_values('Side')
for side in side_list:
data = pipeinfoDF.ix[(label, side)]
print(label, side, data.Nc)
是否可以将unique()单独应用于标签?然后我可以删除标签列表以跟踪哪个标签已经被处理过您的
for
循环将通过.groupby(level=0,
您只是在level=0
上进行分组,因此您生成的数据集将仅在索引的第一级进行排序
您可能可以:
for label in pipedinfoDF.index.unique():
group = pipedinfoDF.loc[label]
要获得所需的顺序。for循环将遍历
.groupby(level=0,
,您只是在level=0上分组,因此生成的数据集将仅在索引的第一级排序。您可能可以在pipedinfoDF.index.sort\u levels()中对标签执行:
只需使用该标签访问分组对象即可获得所需的顺序。Firelynx,太棒了!您已经解决了我的问题!这很有效。尽管排序级别无法识别。因此我只需使用标签pipeinfoDF.index,然后我就可以按正确的顺序访问数据。非常感谢!