Pandas/Pythonic方式到groupby列X,在每个组中,根据列Z中的值返回列Y中的值

Pandas/Pythonic方式到groupby列X,在每个组中,根据列Z中的值返回列Y中的值,python,python-3.x,pandas,pandas-groupby,Python,Python 3.x,Pandas,Pandas Groupby,可复制示例: 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-1

可复制示例:

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(Y列中的所有日期/对象)
和相同的值 组,另一行的值在“Z”列中,其中该组的
Y列中的值是
所有将被硬编码的组肯定存在的某个自定义日期。所以每个小组有两行

在我的输出中,对于组
1
,列
Z
中的值是
10
,因为列
Z
中的值与 组
1
12-15-2015
Y列中所有日期的最小值为
10
。对于同一组
1
,此组的第二行
1
,自定义日期
12-15-2015
Z
中的值也是
10
。对于组
2
min(Y列中的所有日期/对象)
2015-12-11
,对于组
2
和列
Y
中的值,
2015-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..
就可以得到预期的输出,我希望有人能为我提供这样的代码。

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
IIUC

使用-

输出

     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
解释

  • 将所需的自定义日期放入
    date\u fill
  • df.groupby(['X'])['Y'].idxmin()
    Y的
    min
    获取行
  • target\u map
    是一个创建的dict,用于在以后保留
    Z
  • 接下来,扩展
    df_g
    ,使每个备用列都有
    na
  • 如果在
    date\u fill
    中输入了
    df
    中不存在的日期,则
    df\u g=df\u g.bfill()
    会出现两次。在这种情况下,
    target\u map
    将不会填充,最终将获得
    na
  • 我相信这可以进行一些优化,但思考过程应该有助于您继续。

    使用-

    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
    
    解释

  • 将所需的自定义日期放入
    date\u fill
  • df.groupby(['X'])['Y'].idxmin()
    Y的
    min
    获取行
  • target\u map
    是一个创建的dict,用于在以后保留
    Z
  • 接下来,扩展
    df_g
    ,使每个备用列都有
    na
  • 如果在
    date\u fill
    中输入了
    df
    中不存在的日期,则
    df\u g=df\u g.bfill()
    会出现两次。在这种情况下,
    target\u map
    将不会填充,最终将获得
    na

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

    感谢您的努力!在我接受之前,如果不太麻烦的话,我想了解一下你的答案的哪一部分是每组的第二排?因为这只是一个可复制的例子,在我更大的例子中,我必须根据没有任何模式的自定义日期进行过滤!在我的可复制示例中,结果是在我的示例中,对于组
    1
    2015-12-15
    既是
    mindate
    ,也是
    customdate
    。感谢您的努力!在我接受之前,如果不太麻烦的话,我想了解一下你的答案的哪一部分是每组的第二排?因为这只是一个可复制的例子,在我更大的例子中,我必须根据没有任何模式的自定义日期进行过滤!在我的可复制示例中,在我的示例中,对于组
    1
    2015-12-15
    既是
    mindate
    ,也是
    customdate
    。。