【问题标题】:Collecting series from Pandas groupby object从 Pandas groupby 对象中收集系列
【发布时间】:2021-07-04 19:33:55
【问题描述】:

我正在使用如下国际象棋结果的数据框

    Opponent    Date    Time    Result
0   Hikaru  2020.03.02  01:22:54    1
1   Hikaru  2020.03.02  01:22:58    0.5
2   Hikaru  2020.03.03  01:18:17    1
3   Hikaru  2020.03.03  01:19:54    0
4   Hikaru  2020.03.03  01:19:45    1
5   Hikaru  2020.03.03  02:15:23    0.5
6   Anish   2020.03.03  02:21:25    0.5
7   Anish   2020.03.03  02:21:29    0
8   Anish   2020.03.04  15:45:12    1
9   Anish   2020.03.04  15:48:11    0.5
10  Anish   2020.03.04  16:05:01    0.5

现在我想 (1) 按对手分组,(2) 按日期分组(在对手内),(3) 将每个结果的计数制成表格,(4) 给出获得的结果的顺序。前 3 个可以通过 pd.crosstab 获得,例如- 一个完整的例子 -

import pandas as pd

d = {'Opponent': ['Hikaru']*6 + ['Anish']*5,
     'Date': ['2020.03.02']*2 + ['2020.03.03']*6 + ['2020.03.04']*3,
    'Time': ['01:22:54', '01:22:58', '01:18:17', '01:19:54', '01:19:45', '02:15:23', '02:21:25', '02:21:29', '15:45:12', '15:48:11', '16:05:01'],
    'Result': ['1', '0.5', '1', '0', '1', '0.5', '0.5', '0', '1', '0.5', '0.5']}

df = pd.DataFrame(data = d)

pd.crosstab([df['Opponent'], df['Date']],
            df['Result'])

我想要的是与最后一个pd.crosstab 相同的输出,但添加了一个列,显示了两位玩家在当天的比赛中的结果顺序(按时间排序),按时间排序。理想情况下,我希望'1'作为'W',0.5s 作为'D',0s 作为'L',并在列中使用一个长字符串。

期望的输出:


                    Result  0   0.5 1   result_seq
Opponent    Date                
Anish   2020.03.03  1   1   0   DL
        2020.03.04  0   2   1   WDD
Hikaru  2020.03.02  0   1   1   WD
        2020.03.03  1   1   2   WWLD

请注意,在原始数据框中,不保证游戏/结果按时间顺序列出;在原始数据框中,每个变量的数据类型都是str,我想在最终输出中保持这种状态(例如Results 应该保留为'1'、'0'、'0.5'字符串, not '1.0', '0.5, '0.0', Dates 最终应该是字符串;只有实际结果计数可以并且可能是整数)。


我的想法:我想只是按时间排序,然后将专栏作为熊猫系列。问题是如何与对手和日期分组一起(即之后)执行此操作。

【问题讨论】:

    标签: python pandas dataframe pandas-groupby crosstab


    【解决方案1】:

    如果你有这个df:

       Opponent        Date      Time  Result
    0    Hikaru  2020.03.02  01:22:54     1.0
    1    Hikaru  2020.03.02  01:22:58     0.5
    2    Hikaru  2020.03.03  01:18:17     0.0
    3    Hikaru  2020.03.03  01:19:45     1.0
    4    Hikaru  2020.03.03  01:19:54     1.0
    5    Hikaru  2020.03.03  02:15:23     0.5
    6     Anish  2020.03.03  02:21:25     0.5
    7     Anish  2020.03.03  02:21:29     0.0
    8     Anish  2020.03.04  15:45:12     1.0
    9     Anish  2020.03.04  15:48:11     0.5
    10    Anish  2020.03.04  16:05:01     0.5
    

    那么你可以使用.pivot_table()来获取你的结果:

    df_out = df.pivot_table(
        index=["Opponent", "Date"],
        columns="Result",
        aggfunc="size",
        fill_value=0,
    ).rename(columns={0.0: "0", 1.0: "1"})
    
    df_out["result_seq"] = df.groupby(["Opponent", "Date"])["Result"].apply(
        lambda x: "".join({0: "L", 1: "W", 0.5: "D"}[v] for v in x)
    )
    print(df_out)
    

    打印:

    Result               0  0.5  1 result_seq
    Opponent Date                            
    Anish    2020.03.03  1    1  0         DL
             2020.03.04  0    2  1        WDD
    Hikaru   2020.03.02  0    1  1         WD
             2020.03.03  1    1  2       LWWD
    

    编辑:按时间排序值:

    df["tmp"] = pd.to_datetime(df.Date + " " + df.Time)
    df = df.sort_values(by="tmp").drop(columns="tmp")
    
    df_out = df.pivot_table(
        index=["Opponent", "Date"],
        columns="Result",
        aggfunc="size",
        fill_value=0,
    ).rename(columns={0.0: "0", 1.0: "1"})
    
    df_out["result_seq"] = df.groupby(["Opponent", "Date"])["Result"].apply(
        lambda x: "".join({0: "L", 1: "W", 0.5: "D"}[v] for v in x)
    )
    print(df_out)
    

    打印:

    Result               0  0.5  1 result_seq
    Opponent Date                            
    Anish    2020.03.03  1    1  0         DL
             2020.03.04  0    2  1        WDD
    Hikaru   2020.03.02  0    1  1         WD
             2020.03.03  1    1  2       WWLD
    

    【讨论】:

    • 此答案未提供按时间排序的顺序。 (它只是按 W 排序,然后是 D,​​然后是 L。)最后一个 result_seq 是错误的:你有 WWDL 但应该有 LWWD,见我的例子。 (为什么是LWWD?因为2020.03.03对阵Hikaru的结果依次是0、1、1、0.5。)
    • 正如我所解释的,没有内在的排序;顺序是按时间对结果进行排序。首先是DL,因为在 2020 年 3 月 3 日对阵 Anish 时,结果为 0.5,按顺序为 0
    • 您的解决方案很好地解决了原始 df。但是,我还表示 df 不一定已经按时间排序,并且您的答案也没有明确按时间排序。我已经更新了问题中的 df 以清楚地反映这个额外的困难(请注意,2020.03.03 对 Hikaru 的结果现在已经交换了 01:19:54 比赛和 01:19:45 比赛的行)
    • 优秀,满足所有要求:)
    【解决方案2】:

    你在正确的轨道上。只需用所需的标签重命名列:

    df1=pd.crosstab([df['Opponent'], df['Date']],
                df['Result']).reset_index().rename(columns={1.0:'W',0.5:'D',0.0:'L'})
    

    创建新列,在其中根据列值连接相关列的一部分

    df1['result_seq'] = df1.iloc[:,2:].mul(df1.iloc[:,2:].columns.values).sum(axis=1)
    
    
    
    Result Opponent        Date  L  D  W result_seq
    0         Anish  2020.03.03  1  1  0         LD
    1         Anish  2020.03.04  0  2  1        DDW
    2        Hikaru  2020.03.02  0  1  1         DW
    3        Hikaru  2020.03.03  1  1  2       LDWW
    

    【讨论】:

    • 我收到错误UFuncTypeError: ufunc 'multiply' did not contain a loop with signature matching types (dtype('<U21'), dtype('<U21')) -> dtype('<U21')。请尝试在我提供代码的原始 df 上运行
    • 我怀疑,很可能您有更多列,或者您的列没有按我的方式排列,因此切片不起作用。让我们试试df1['result_seq'] = df1[['L','D','W']].mul(df1[['L','D','W']].columns.values).sum(axis=1)
    • 同样的错误,抱歉。不,没有更多的列。正是我在问题中提供的确切df(带有代码)。
    • 啊哈,你运行的是什么版本,因为我无法预先产生错误?
    猜你喜欢
    • 2014-10-06
    • 2014-06-02
    • 1970-01-01
    • 2020-10-04
    • 1970-01-01
    • 2019-11-18
    • 2019-08-03
    • 2015-10-10
    • 2018-08-13
    相关资源
    最近更新 更多