汇总数据帧特征的替代方法(嵌套循环、groupby)

Alternative ways to summarize dataframe feature (nested for loops, groupby)

提问人:Meera s 提问时间:11/11/2023 最后编辑:OCaMeera s 更新时间:11/19/2023 访问量:223

问:

下面的数据框表示一个月的客户存在,粒度为 1 小时。存在时间记录为两列和 。 是月份中的某一天,是一天中的小时数。"REGIS_DAY""REGIS_HOUR""REGIS_DAY""REGIS_HOUR"

REGIS_DAY REGIS_HOUR  REGIS_DATE     REGIS_TIME
7             16       7/10/2011     16:21:05
3             3        3/10/2011     3:57:45
16            4        16/10/2011    4:08:47
24            3        24/10/2011    3:09:47
29            13       29/10/2011    13:43:40
7             16       7/10/2011     16:41:05
3             3        3/10/2011     3:24:45
3             3        3/10/2011     3:24:00
29            13       29/10/2011    13:43:01
29            13       29/10/2011    13:10:40
29            13       29/10/2011    13:20:40

输入数据: (df.to_dict())

{'REGIS_DAY': {0: '7', 1: '3', 2: '16', 3: '24', 4: '29', 5: '7', 6: '3', 7: '3', 8: '29', 9: '29', 10: '29'}, 
 'REGIS_HOUR': {0: '16', 1: '3', 2: '4', 3: '3', 4: '13', 5: '16', 6: '3', 7: '3', 8: '13', 9: '13', 10: '13'}, 
 'REGIS_DATE': {0: '7/10/2011', 1: '3/10/2011', 2: '16/10/2011', 3: '24/10/2011', 4: '29/10/2011', 5: '7/10/2011', 6: '3/10/2011', 7: '3/10/2011', 8: '29/10/2011', 9: '29/10/2011', 10: '29/10/2011'}, 
 'REGIS_TIME': {0: '16:21:05', 1: '3:57:45', 2: '4:08:47', 3: '3:09:47', 4: '13:43:40', 5: '16:41:05', 6: '3:24:45', 7: '3:24:00', 8: '13:43:01', 9: '13:10:40', 10: '13:20:40'}}

在此任务中,我需要通过循环计算每小时同时在场的客户数量。因此,我每天(即 31 小时)和每 24 小时迭代一次:for

data_3124 = np.zeros((31,24), dtype = float)
for i in range(31):
    for j in range(24):
        number = (df.REGIS_DAY == (i+1)) & (df.REGIS_HOUR == j)
        data_3124[i,j] = number.sum()

问题:上面的代码为所有值返回 0。

预期输出

REGIS_DAY   REGIS_HOUR    sum
7             16           2
3             3            3
29            13           4
16            4            1
24            3            1

这就像在第 7 天和第 16 小时一样,同时在场的客户总数为 2 人;同样,在第 3 天和第 3 小时,有 3 名客户。

我也尝试过这个功能。我知道我找到解决方案的方式是错误的。.isin


其他信息:

OCaKirill Kondratenko 的无循环解决方案

 df_result = (df_2011
      .groupby(['REGIS_DAY', 'REGIS_HOUR'])
      .size()
      .reset_index()
      .rename(columns={0:'sum'}))

df1 = df_2011.groupby(by=['REGIS_DAY','REGIS_HOUR'], as_index=False).size()

产量低于预期/正确的结果。那么,为什么我的嵌套循环方法总和总是 0?

熊猫 数据帧 for循环 分组依据

评论

0赞 OCa 11/13/2023
你好。关于问题被关闭,您得到了什么反馈?碰巧的是,在此之前,我曾建议对您的问题进行编辑,在我看来,这大大提高了清晰度。我相信我们可以等待我的编辑被审稿人接受。你有答案吗?

答:

0赞 Kirill Kondratenko 11/11/2023 #1

看起来你需要:

df = (df
      .groupby(['REGIS_DAY', 'REGIS_HOUR'])
      .size()
      .reset_index()
      .rename(columns={0:'sum'}))

print(df)

输出:

   REGIS_DAY    REGIS_HOUR  sum
0   3                3      3
1   7               16      2
2   16               4      1
3   24               3      1
4   29              13      4

如果仍要使用循环,请使用:

REGIS_DAY = []
REGIS_HOUR = []
sum = []
for i in range(31):
    for j in range(24):
        REGIS_DAY.append(i)
        REGIS_HOUR.append(j)
        sum.append(len(df[(df['REGIS_DAY'] == i) & (df['REGIS_HOUR'] == j)]))
        
df_new = pd.DataFrame({'REGIS_DAY': REGIS_DAY,
                       'REGIS_HOUR': REGIS_HOUR,
                       'sum': sum})

评论

0赞 Meera s 11/11/2023
如何使用循环?对于范围 (31) 中的 i: 对于范围 (24) 中的 J:
0赞 OCa 11/11/2023
你好。将 groupby 重新分配为 df 意味着覆盖和丢失数据。如果 OP 以后需要他们的其他数据怎么办?
0赞 Kirill Kondratenko 11/11/2023
@Meeras我更新了我的循环解决方案答案
0赞 Meera s 11/11/2023
df = (df .groupby(['REGIS_DAY', 'REGIS_HOUR']) .size() .reset_index() .rename(columns={0:'sum'})) 这给出了正确/预期的结果。但是使用循环时,所有总和列为 0
0赞 Meera s 11/11/2023
我的作业需要提交带有循环的解决方案。我来自不同的语言背景,学习 Python 进行数据分析是我职业转变的一部分。将数据作为一个整体来处理而不是逐行读取对我来说是令人困惑的。感谢您的时间和快速回复
0赞 OCa 11/11/2023 #2

1. 正确设置数据类型

您正在将数值方法应用于仅包含字符串的数据。这就是为什么您的尝试返回空表的原因。整数将不匹配 string 。虽然某些方法不关心数据类型(请参阅下面的方法),但 numpy 数组上的嵌套循环确实需要实际数字。因此,您必须从以下数据类型转换开始:1'1'groupby

df[['REGIS_DAY','REGIS_HOUR']] = df[['REGIS_DAY','REGIS_HOUR']].astype('int')

df.dtypes

REGIS_DAY      int32
REGIS_HOUR     int32
REGIS_DATE    object
REGIS_TIME    object
dtype: object

现在你可以工作了。
有关该主题的更多信息,请参阅例如更改 pandas 中的列类型


2. 关于您的第一次尝试

您的代码片段已正确编写,并返回预期的 31*24 数组。它看起来不是收集数据的最便捷方式,以便您可以进一步使用它们。

# This was fine all along:
data_3124 = np.zeros((31,24), dtype = float)
for i in range(31):
    for j in range(24):
        number = (df.REGIS_DAY == (i+1)) & (df.REGIS_HOUR == j)
        data_3124[i,j] = number.sum()

您可以通过几次检查来探测它:

data_3124.sum()
11
# This is indeed the number of rows in the initial dataframe

data_3124[6,16]
2.0
# Number of hits for day 7, hour 16.

3. pythonic 的做事方式

现在,它需要一行来定义另一个数据帧,就像您需要的那样:(不需要从字符串转换为整数即可工作)groupby

df1 = df.groupby(by=['REGIS_DAY','REGIS_HOUR'], as_index=False).size()

   REGIS_DAY  REGIS_HOUR  size
0          3           3     3
1          7          16     2
2         16           4     1
3         24           3     1
4         29          13     4

如果您仍然想通过循环解决这个问题,我们也可以研究一下,但要知道这将是推荐的方法。
如需进一步阅读,您可以尝试: Pandas sum by groupby,但排除某些列


4. 嵌套循环的建议

接下来是一个可能更适合您当前任务的解决方案。它还会产生您想要的输出。同样,这需要事先将字符串转换为整数,如答案的第一部分所述。

这里的原则是以单行数据帧的形式填充每个点击(有客户在场的小时)的列表,然后将所有点击重新组合到一个最终的数据帧中(不太关心代码效率)。

df_list = []

for i in range(31):
    for j in range(24):
        # view dataframe subset meeting condition
        df_ij = df.loc[(df['REGIS_DAY']==i+1) & (df['REGIS_HOUR']==j)]
        # its number of rows
        s_ij = len(df_ij)
        # assign it to table
        if s_ij>0:
            df_list.append(pd.DataFrame({'REGIS_DAY'  : [i],
                                         'REGIS_HOUR' : [j],
                                         'sum'        : [s_ij]}))
# Concatenate the list of dataframes into a single table.
pd.concat(df_list, axis=0)

   REGIS_DAY  REGIS_HOUR  sum
0          2           3    3
0          6          16    2
0         15           4    1
0         23           3    1
0         28          13    4

为了进一步阅读,我建议创建一个空的 Pandas DataFrame,然后填充它

对于良好的做法,请避免将列命名为 pandas 函数。这里是 sum,因为语法 df.sum 可能无法提供您期望的内容。将列命名为“Sum”足以消除歧义。