亲爱的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, 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
但是,当我想遍历此multindex数据框的行时,通常会出现此问题
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()语句显示数据帧已正确排序,但是在遍历索引(我通常这样做是将数据复制到另一张表)上时,似乎并没有使用正确的排序索引。
在我看来,这似乎是个错误:groupby语句中的sort选项对结果没有影响,并且据报道here也有类似的情况。
现在我的问题是:有没有简单的方法来解决此问题? 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
但是,随着前面遍历多指标帧的循环,它再次在第一组上进行排序,而我使用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_index保持MultiIndex中级别的顺序,但仅更改标签的顺序。
因此,我可以通过强制标签以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
哪个是正确的!
因此,我发现了一个非常丑陋的解决方法:排序之后,将整个数据框复制到一个新的数据框中,重置索引,然后再复制所有内容以将MultiIndex标签强制为数字顺序。但是我认为这是非常低效的,会生成很多代码,并且我相信有可能更高效。
因此,我的问题是:是否有一种方法可以在考虑标签顺序的情况下遍历多索引数据框的行?显然它被忽略了。我想念什么吗?希望有一种更简单的方法可以做到这一点。
任何提示表示赞赏!
编辑:
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选项不会产生相同的结果?这是一个错误,还是我错过了使用groupby方式遍历我的数据框的问题。它基于我在Google上搜索过的示例,但应谨慎使用。
无论如何,现在我解决了我的问题,我将通过访问数据的方式来放弃我的小组。
编辑:
Firelynx的解决方案有效,但是,它不再考虑多级结构,而只是将所有Label和side级放在一个列表中。
为了获得与我想要的groupby方法非常相似的内容,我现在进行以下修改
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的最新建议,我进行了一个小更新,使其更简洁。尽管如此,您仍需要保留一个列表以防止重复计算标签,因为唯一性仅适用于唯一性(标签,侧面)组合。所以我现在有
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()单独应用于标签?然后,我可以删除label_list来跟踪已经处理过的标签
最佳答案
您的for
循环经过.groupby(level=0, ...,
,您只是在level=0
上分组,因此,结果数据集将仅在索引的第一级上排序。
您可能可以这样做:
for label in pipedinfoDF.index.unique():
group = pipedinfoDF.loc[label]
获得所需的订单。
关于python - python multiandas排序后的 Pandas groupby不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32005508/