如何解决Pandas DataFrame.agg 在选择缺少类别后生成多索引
我仍在研究可重现的示例,但不幸的是,我是 Pandas 的绝对初学者,我对自己做错了什么感到迷茫。该行为似乎是 Pandas 1.2.4 中的一个错误。
我有两个数据框,files_df
提供了多个存储库中源代码文件的类型和复杂性,而 proj_df
提供了项目及其上次更新时间。
我试图按复杂性为每种语言挑选前两个项目,它看起来像这样:
def largest(x):
return (
# Pick the top two rows in each language (from the groupby) by scc_complexity
x.nlargest(2,'scc_complexity')
# Combine the proj_repo values (we don't actually care about scc_complexity at this point)
.agg({'proj_repo': lambda y: '<br>'.join(y.values)})
)
max_proj = (
# Pick the language and complexity columns
files_df[['scc_lang','scc_complexity','proj_repo']]
# Sum scc_complexity by language and repo
.groupby(['scc_lang','proj_repo'],observed=True).sum()
.reset_index()
)
max_proj = (
max_proj
# Select only repos updated in the last two years
.loc[max_proj['proj_repo'].isin(proj_df.loc[proj_df['last_author_time'] > '2019-05-01','proj_repo'])]
# Now pick the top repos by language complexity
.groupby('scc_lang').apply(largest)
)
这应该会生成一个以 scc_lang
作为其索引和 proj_repo
作为其唯一列的框架,但它会生成以下形状:
<class 'pandas.core.frame.DataFrame'>
MultiIndex: 151 entries,('ASP','proj_repo') to ('XML Schema (min)','proj_repo')
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 0 151 non-null object
1 proj_repo 0 non-null object
dtypes: object(2)
memory usage: 8.7+ KB
MultiIndex([( 'ASP','proj_repo'),( 'Autoconf',( 'BASH',( 'Batch',...
也就是说,现在有一个多索引,proj_info
列现在是一个名为 0
的列,而 proj_info
列现在是空的。
现在是真正奇怪的部分。如果您从 .loc[]
中删除 max_proj
选择,此代码会生成正确的形状。对于我的一生,我无法弄清楚为什么进行选择会改变结果的形状。我无法用一个完整的小例子来重现结果。
我会继续尝试,如果我学到任何有趣的东西,我会在这里报告。
轻微更新:我发现删除 observed=True
似乎也能产生正确的形状——所以这可能与缺少键有关?
更大的更新:我设法重现了它,是的,它确实与丢失的密钥有关,但我不确定如何:
tdf = pd.DataFrame([['lang1',1,'ab'],['lang2',2,'cd'],4,3,'ef'],'gh'],['lang3','ef']
],columns=['lang','size','name']).astype({'lang': 'category'},copy=False).set_index('lang')
d = tdf.groupby(['lang','name'],observed=True).sum().reset_index()
d = d.loc[d['name'].isin(pd.Series(['ab','cd','gh']))].groupby('lang').apply(lambda x: x.agg({'name': lambda y: ' '.join(y.values)}))
display(d)
d.info()
0 | 姓名 | ||
---|---|---|---|
语言 | |||
lang1 | 姓名 | ab | NaN |
lang2 | 姓名 | cd gh | NaN |
<class 'pandas.core.frame.DataFrame'>
MultiIndex: 2 entries,('lang1','name') to ('lang2','name')
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 0 2 non-null object
1 name 0 non-null object
dtypes: object(2)
memory usage: 356.0+ bytes
删除“ab”或“ef”名称会触发此操作,这意味着它与缺少的类别有关。事实上,将列类型改回 object
也 解决了这个问题。所以我似乎误解了类别...
另一个更新:我已设法将其缩减为这两行:
tdf2 = pd.DataFrame([['a','boof']],'name']).astype({'lang': pd.CategoricalDtype(['a','b'])})
display(tdf2.groupby('lang').apply(lambda x: x.agg({'name': lambda y: y.values})))
我们现在非常小。删除上述代码中的几乎任何一点都会导致生成的数据框对齐到正确的形状。
我们这里有一个 agg 应用于由 .groupby('lang')
生成的空数据帧,它复活了一个已从数据中删除的类别。在 apply()
内重新加入结果似乎有些奇怪。
这里的一个新发现是从 y.values
lambda 返回 apply()
是其中的一部分:如果我返回 y
(系列),则不会发生奇怪的结构。
解决方法
终于找到了。奇怪的结构结果是因为空数据帧上的单列agg的结果是一个DataFrame,但如果DataFrame有行,结果是一个Series:
tdf2 = pd.DataFrame([],columns=['lang','name'])
print(type(tdf2.agg({'name': lambda y: y.values})))
tdf2 = pd.DataFrame([['a','boof']],'name'])
print(type(tdf2.agg({'name': lambda y: y.values})))
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.series.Series'>
groupby 的 apply()
返回奇怪的结构,因为它试图将两者结合起来:
tdf2 = pd.DataFrame([['a','boof'],['b','toop']],'name'])
print(tdf2.groupby('lang').apply(lambda x: x['name'] if x.iloc[0,0] == 'a' else x))
0 lang name
lang
a 0 boof NaN NaN
b 1 NaN b toop
我不知道我是否会称其为错误。这当然是令人惊讶的行为,一般来说程序should not be surprising。
更新:归档为 https://github.com/pandas-dev/pandas/issues/41672。
更新 2:现在我了解了类别的工作原理,对原始代码的最终修复是在应用之前将 observed=True
添加到 groupby,消除空数据框。 >
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。