【问题标题】:Pivot across multiple columns with repeating values in each column跨多列透视,每列中具有重复值
【发布时间】:2019-07-23 20:20:22
【问题描述】:

我正在尝试旋转 pandas 数据框,但数据遵循一种我似乎无法旋转的奇怪格式。数据结构如下:

Date, Location, Action1, Quantity1, Action2, Quantity2, ... ActionN, QuantityN
<date>   1      Lights    10         CFloor     1        ...  Null     Null
<date2>  2      CFloor    2          CWalls     4        ...  CBasement 15 
<date3>  2      CWalls    7          CBasement  4        ...  NUll     Null

从本质上讲,每个动作都将始终附加一个数量(可能为 0),但 null 动作永远不会有数量(数量将为空)。我试图实现的格式如下:

       Lights    CFloor    CBasement   CWalls
1        10         1         0           0
2        0          2         19          11

行的索引成为位置,而列成为跨多个活动列找到的任何唯一操作。将数据拉到一起时,每行/列的值是与动作关联的每个数量的总和(即 Action1 对应于 Quantity1)。有没有办法使用原生 pandas 枢轴功能来做到这一点?

我当前的代码对所有活动列执行 ravel 以获取所有唯一活动的列表。它还将从 Location 列中获取所有唯一位置。拥有唯一列后,我会创建一个空数据框并用零填充:

       Lights    CFloor    CBasement   CWalls
1        0         0         0            0
2        0         0         0            0

然后我使用 itertuples() 方法迭代旧数据帧(我被告知它比 iterrows() 快得多)并填充新数据帧。这个空的数据框充当模板,存储在内存中并稍后填充。

#Creates a template from the dataframe
def create_template(df):
    act_cols = ['Activity01', 'Activity02', 'Activity03', 'Activity04']
    activities = df[act_cols]
    flat_acts = activities.values.ravel('K')
    unique_locations = pd.unique(df['Location'])
    unique_acts = pd.unique(flat_acts)
    pivot_template = pd.DataFrame(index=unique_locations, columns=unique_acts).fillna(0)
    return pivot_template


#Fills the template from the dataframe
def create_pivot(df, pivot_frmt):
    act_cols = ['Activity01', 'Activity02', 'Activity03', 'Activity04']
    quant_cols = ['Quantity01', 'Quantity02', 'Quantity03', 'Quantity04']

    for row in df.itertuples():
        for act, quantity in zip(act_cols, quant_cols):
            act_val = getattr(row, act)
            if pd.notna(act_val):
                quantity_val = getattr(row, quantity)
                location = getattr(row, 'Location')
                pivot_frmt.loc[location, act_val] += quantity_val
    return pivot_frmt

虽然我的解决方案有效,但在处理大型数据集时速度非常慢,并且需要 10 秒或更长时间才能完成此类操作。任何帮助将不胜感激!

【问题讨论】:

  • 更新:经过一番研究,我发现交叉表函数在传递值时似乎具有与枢轴相同的功能。我的初始测试表明它按我的预期工作,但一次只能处理一列和一个值集。这是我的测试代码: pd.crosstab(index=df['Location'], columns=df['Activity01'], values=df['Quantity01'], aggfunc=np.sum).fillna(0)跨度>

标签: python pandas


【解决方案1】:

在尝试了各种 pandas 功能(例如在多个列上同时熔化和旋转)后,我找到了一个适合我的解决方案:

对于每个数量-活动对,我构建了最终数据集的部分框架并将其存储在列表中。处理完每一对后,我将得到多个数据帧,这些数据帧都具有相同的行数,但列数可能不同。我通过简单地连接列解决了这个问题,如果有任何列重复,我将它们相加得到最终结果。

def test_pivot(df):
    act_cols = ['Activity01', 'Activity02', 'Activity03', 'Activity04']
    quant_cols = ['Quantity01', 'Quantity02', 'Quantity03', 'Quantity04']
    dfs = []
    for act, quant in zip(act_cols, quant_cols):
        partial = pd.crosstab(index=df['Location'], columns=df[act], values=df[quant], aggfunc=np.sum).fillna(0)
        dfs.append(partial)

    finalDf = pd.concat(dfs, axis=1)
    finalDf = test.groupby(finalDf.columns, axis=1).sum()
    return finalDf

我在此方法中做了两个假设:

  1. 索引在所有部分数据帧中保持顺序
  2. 所有部分数据帧都有相同数量的索引

虽然这可能不是最优雅的解决方案,但它实现了预期的结果,并大大减少了处理数据所需的时间(从 10 秒 ~4k 行减少到 0.2s ~4k 行)。如果有人有更好的方法来处理这种情况并一次性完成上述过程,那么我很乐意看到您的回复!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多