Pandas 箱内的零填充

Zero padding inside of Pandas bins

我有一个 Pandas 数据框 df,如下所示:

df = pd.DataFrame(data={"Group": ["a", "a", "a", "b", "b"], 
                       "Index": [1, 8, 9, 1, 3], 
                       "Value": [23.2, 1.2, 1.7, 21.9, 432.2]})
>>> print(df)

  Group  Index  Value
0     a      1   23.2
1     a      8    1.2
2     a      9    1.7
3     b      1   21.9
4     b      3  432.2

我想获取以下信息(空行是为了说明):

  Group  Index   Value
0     a      1   23.2
1     a      2    0.0
2     a      3    0.0

3     a      7    0.0
4     a      8    1.2
5     a      9    1.7

6     b      1   21.9
7     b      2    0.0
8     b      3  432.2

基本上我想在“值”列中引入零填充,以便所有“索引”容器 [1、2、3]、[4、5、6] 和 [7、8、9]恰好填充了 3 个值。 “Index”整数在组内只能出现一次,范围为 1 到 9。如果 bin 不包含任何“Index”整数,则不应进行填充。 “a组”和“b组”的行应该独立处理。

这是我得到的结果:

a_group = df.groupby("Group")
for _, group in a_group:
    group["Bin"] = pd.cut(group["Index"], bins=list(range(1, 12, 3)), right=False)
    print(group)
    print("\n")

  Group  Index  Value      Bin
0     a      1   23.2   [1, 4)
1     a      8    1.2  [7, 10)
2     a      9    1.7  [7, 10)


  Group  Index  Value     Bin
3     b      1   21.9  [1, 4)
4     b      3  432.2  [1, 4)

此时我不知道还能做什么,希望能得到一些帮助。提前致谢。

这是您要找的吗?

g = df['Group'].unique()
i = range(1,df['Index'].max()+1)

df2 = df.set_index(['Group','Index']).reindex(pd.MultiIndex.from_product([g,i])).assign(cc = lambda x: (x.groupby(level=0).cumcount())//3).rename_axis(['Group','Index'],axis=0)
df2.loc[~df2['Value'].isna().groupby([pd.Grouper(level=0),df2['cc']]).transform('all')].reset_index().fillna(0).drop('cc',axis=1)

@rhug123 接受的答案是一个很好的答案,但我在下面提出了一个替代解决方案。我的解决方案的核心思想是创建一个 bin identifier/key 来帮助 select 目标索引。

TL;DR

max_index_value = 9
bin_size = 3

df_temp = pd.DataFrame({"Group":np.repeat(df['Group'].unique(), max_index_value)})
df_temp['Index'] = df_temp.groupby(['Group']).cumcount() + 1
df_temp = df_temp.merge(df, how='left', on=['Group', 'Index'])
df_temp['bin_key'] = df_temp.groupby(['Group']).cumcount() // bin_size

df_bin_key = df_temp.groupby(['Group', 'bin_key'])[['Value']].max().reset_index().dropna().drop(columns=['Value'])

df_final = df_bin_key.merge(df_temp, how='left', on=['Group', 'bin_key']).fillna(0).drop(columns=['bin_key'])

分步说明。

首先创建一个临时数据框,用作原始 df.

缺失索引值的占位符
max_index_value = 9
bin_size = 3

df_temp = pd.DataFrame({"Group":np.repeat(df['Group'].unique(), max_index_value)})
df_temp['Index'] = df_temp.groupby(['Group']).cumcount() + 1

# getting the values from original dataset
df_temp = df_temp.merge(df, how='left', on=['Group', 'Index'])

接下来,添加用于标记 bin 以供稍后分组的 bin 键。

# the bin size defines the available key values
df_temp['bin_key'] = df_temp.groupby(['Group']).cumcount() // bin_size
Group      Index       bin_key   Value
a          1           0            23.2
a          2           0            NaN
a          3           0            NaN
a          4           1            NaN
.          .           .            .
.          .           .            .
.          .           .            .
b          6           1            NaN
b          7           2            NaN
b          8           2            NaN
b          9           2            NaN

然后,这是棘手的部分,我们在 Groupbin_key 列上使用组函数,并使用 max() 函数 select 哪个组和键应该出现在最终结果中。

df_bin_key = df_temp.groupby(['Group', 'bin_key'])[['Value']].max().reset_index().dropna().drop(columns=['Value'])
Group   bin_key
a       0
a       2
b       0

最后,我们通过使用 Groupbin keys.

df_temp 左连接到 df_bin_key 来达到预期的结果
df_final = df_bin_key.merge(df_temp, how='left', on=['Group', 'bin_key']).fillna(0).drop(columns=['bin_key'])
Group   Index   Value
a       1       23.2
a       2       0.0
a       3       0.0
a       7       0.0
a       8       1.2
a       9       1.7
b       1       21.9
b       2       0.0
b       3       432.2

PS:为了便于说明,我将这个解决方案分解为多个步骤,这里的一些步骤可以重写并组合成一行。