【问题标题】:Pandas/Pythonic way to groupby a column X, within each group, return value in column Y based on value in column ZPandas/Pythonic 方法按列 X 分组,在每个组内,根据 Z 列中的值返回 Y 列中的值
【发布时间】:2018-08-04 18:44:10
【问题描述】:

可重现的例子:

df = pd.DataFrame([[1, '2015-12-15', 10],
                   [1, '2015-12-16', 13], 
                   [1, '2015-12-17', 16], 
                   [2, '2015-12-15', 19],
                   [2, '2015-12-11', 22], 
                   [2, '2015-12-18', 25],
                   [3, '2015-12-14', 28], 
                   [3, '2015-12-12', 31], 
                   [3, '2015-12-15', 34]])

df.columns = ['X', 'Y', 'Z']
print(df.dtypes)
print()
print(df)

可重现示例的输出和每列的数据类型:

X     int64
Y    object
Z     int64
dtype: object

   X           Y   Z
0  1  2015-12-15  10
1  1  2015-12-16  13
2  1  2015-12-17  16
3  2  2015-12-15  19
4  2  2015-12-11  22
5  2  2015-12-18  25
6  3  2015-12-14  28
7  3  2015-12-12  31
8  3  2015-12-15  34

预期输出:

   X           Y   Z
0  1  2015-12-15  10
1  1  2015-12-15  10
2  2  2015-12-11  22
3  2  2015-12-15  19
4  3  2015-12-12  31
5  3  2015-12-15  34

该输出的解释:

对于按X 分组后X 列中的每个组,我想要一行具有Z 列中的值 其中,该组的 Y 列中的值是 min(all dates/object in column Y) 并且相同 组,另一行在“Z”列中具有值,其中该组的列Y 中的值是some custom date that definitely exists for all groups which will be hardcoded。所以每个组都会有两行。

在我的输出中,对于组1Z 列中的值是10,因为Z 列中的值与 对于112-15-2015 组,Y 列中所有日期的最小值是 10。对于同一组1,该组1 的第二行,Z 列中自定义日期12-15-2015 的值也是10。对于2 组,min(all dates/objects in column Y)2015-12-112Z 列中对应的值为Y2015-12-11 列中的值为22。而对于自定义日期12-15-2015,它是19

这是我假设的一些线性时间搜索/延迟代码,我为此编写了一些代码:

uniqueXs = list(dict(Counter(df['X'].tolist())).keys()) #Get every unique item in column X is a list. 
df_list = [] #Empty list that will have rows of my final DataFrame

for x in uniqueXs: #Iterate through each unique value in column X

    idfiltered_dataframe = df.loc[df['X'] == x] #Filter DataFrame based on the current value in column X 
                                                #(iterating through list of all values)

    min_date = min(idfiltered_dataframe['Y']) #Min of column Y
    custom_date = '2015-12-15' #Every group WILL have this custom date.

    mindatefiltered_dataframe = idfiltered_dataframe.loc[idfiltered_dataframe['Y'] == min_date] #Within group, filter rows where column Y has minimum date
    customdatefiltered_dataframe = idfiltered_dataframe.loc[idfiltered_dataframe['Y'] == custom_date]  #Within group, filter rows where column Y has a custom date

    for row_1 in mindatefiltered_dataframe.index: #Iterate through mindatefiltered DataFrame and create list of each row value required

        row_list = [mindatefiltered_dataframe.at[row_1, 'X'], mindatefiltered_dataframe.at[row_1, 'Y'], mindatefiltered_dataframe.at[row_1, 'Z']]
        df_list.append(row_list) #Append to a master list

    for row_2 in customdatefiltered_dataframe.index: #Iterate through customdatefiltered DataFrame and create list of each row value required

        row_list = [customdatefiltered_dataframe.at[row_2, 'X'], customdatefiltered_dataframe.at[row_2, 'Y'], customdatefiltered_dataframe.at[row_2, 'Z']]
        df_list.append(row_list) #Append to a master list



print(pd.DataFrame(df_list)) #Create DataFrame out of the master list

我的印象是有一些巧妙的方法,您只需执行 df.groupby.. 并获得预期的输出,我希望有人可以为我提供此代码。

【问题讨论】:

    标签: python python-3.x pandas pandas-groupby


    【解决方案1】:

    IIUC

    g1=df.groupby('X').Y.value_counts().count(level=1).eq(df.X.nunique()) # get group1 , all date should show in three groups , we using value_counts
    df.Y=pd.to_datetime(df.Y) # change to date format in order to sort
    g2=df.sort_values('Y').groupby('X').head(1) # get the min date row . 
    
    pd.concat([df.loc[df.Y.isin(g1[g1].index)],g2]).sort_index() # combine all together 
    Out[280]: 
       X          Y   Z
    0  1 2015-12-15  10
    0  1 2015-12-15  10
    3  2 2015-12-15  19
    4  2 2015-12-11  22
    7  3 2015-12-12  31
    8  3 2015-12-15  34
    

    【讨论】:

    • 感谢您的努力!在我接受之前,如果不是太麻烦,我想了解您的答案的哪一部分我们得到了每组的第二行?因为这只是一个可重现的示例,所以在我更大的示例中,我必须根据没有任何模式的自定义日期进行过滤!在我的可重现示例中,事实证明,在我的示例中,对于组 12015-12-15 既是 mindate 也是 customdate..
    【解决方案2】:

    使用 -

    date_fill = dt.datetime.strptime('2015-12-15', '%Y-%m-%d')
    df['Y'] = pd.to_datetime(df['Y'], format='%Y-%m-%d')
    
    df_g = df.loc[df.groupby(['X'])['Y'].idxmin()]
    df2 = df[df['Y']==date_fill]
    target_map = pd.Series(df2['Z'].tolist(),index=df2['X']).to_dict()
    df_g.index = range(1, 2*len(df_g)+1, 2)
    df_g = df_g.reindex(index=range(2*len(df_g)))
    df_g['Y'] = df_g['Y'].fillna(date_fill)
    df_g = df_g.bfill()
    df_g.loc[df_g['Y']==date_fill, 'Z'] = df_g[df_g['Y']==date_fill]['X'].map(target_map)
    df_g = df_g.bfill()
    print(df_g)
    

    输出

         X          Y     Z
    0  1.0 2015-12-15  10.0
    1  1.0 2015-12-15  10.0
    2  2.0 2015-12-15  19.0
    3  2.0 2015-12-11  22.0
    4  3.0 2015-12-15  34.0
    5  3.0 2015-12-12  31.0
    

    说明

    1. 将所需的自定义日期放入date_fill
    2. df.groupby(['X'])['Y'].idxmin()minY 取行
    3. target_map 是为稍后保留 Z 值而创建的字典
    4. 接下来,df_g 被扩展为每个备用列都有 na
    5. 如果您在date_fill 中输入的日期在df 中不存在,df_g = df_g.bfill() 会出现两次。在这种情况下,target_map 不会填充,您最终将获得 na 值。

    我相信这可以在一定程度上进行优化,但思考过程应该可以帮助您继续进行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-31
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      相关资源
      最近更新 更多