亲爱的蟒蛇/熊猫专家
我遇到了对多索引pandas数据帧的索引进行排序的问题。更具体地说,似乎排序有效,但之后的分组操作是“忘记”。再次排序。供参考:我正在运行python 3.4.3(由anaconda)和pandas 0.16.2(np19py34_0)。
现在详细说明我要做的事情。
我创建了一个多索引数据框
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()语句显示数据框已经正确排序,而循环索引(我通常将数据复制到另一个表)似乎没有使用正确的排序索引。
这对我来说似乎是一个错误:groupby语句中的sort选项对结果没有影响,并且报告了类似的事情here。
现在我的问题是:有一种简单的方法可以解决这个问题吗? 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=[])
然后像我之前那样填充其余的字段。这样,多索引看起来像
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搜索的示例,但应谨慎使用。 无论如何,现在我解决了我的问题,我将放弃我的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的最新建议,我有一个小的更新,使它更清洁。但是,您需要保留一个列表以防止标签的重复计数,因为唯一适用于唯一(标签,侧面)组合。所以我现在有了
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以跟踪已经处理的标签
答案 0 :(得分:1)
您的for
循环超过.groupby(level=0, ...,
,您只是在level=0
进行分组,因此您的结果数据集将仅在索引的第一级进行排序。
您可以这样做:
for label in pipedinfoDF.index.unique():
group = pipedinfoDF.loc[label]
获得您想要的订单。