数据框:将基于行的交易数据转换为每个日期的汇总

时间:2020-06-05 10:09:06

标签: python-3.x pandas dataframe formatting

我以以下格式从SQLITE数据库检索数据(并将其转换为熊猫数据框):

Driver | Date loading | Date unloading | Loading Adress | Unloading Address
Peter  | 02.05.2020   | 03.05.2020     | 12342, Berlin  | 14221, Utrecht
Peter  | 03.05.2020   | 04.05.2020     | 14221, Utrecht | 13222, Amsterdam
Franz  | 03.05.2020   | 03.05.2020     | 11111, Somewher| 11221, Somewhere2
Franz  | 03.05.2020   | 05.05.2020     | 11223, Upsalla | 14231, Berlin

可以为查询指定日期范围,以便它概述哪个驱动程序在指定的日期范围内按日期排序提供哪些运输工具。

我要做的转换目标是每个驾驶员的每周计划,而该日期范围将在可用列中进行排序。因此,对于上面的数据,它看起来如下所示:

Driver | 02.05.2020           | 03.05.2020            | 04.05.2020         | 05.05.2020      |
Peter  | Loading:             | Unloading:              Unloading:
         12342, Berlin          14221, Utrecht          13222, Amsterdam
                                Loading:
                                14221, Utrecht

Franz  |                      | Loading:              |                    | Unloading:
                                11111, Somewher                              14231, Berlin
                                Unloading:
                                11221, Somewhere2
                                Loading:
                                11223, Upsalla

有什么方法可以通过数据框操作来实现所描述的输出?在单个数据列中,我需要保持顺序:先加载,然后卸载,然后在日期相同的情况下转到下一个数据行。

2 个答案:

答案 0 :(得分:2)

我将编写伪伪的解决方案,实际上它是一个解决方案,它只缺少一个实体task_id,我将在后面详细说明。我将把您的日期框架(有问题的第一个)称为df,并将转换后的版本创建为t_df。该t_df将是一个统一的日期和地址表。

我将创建一个数据框,如下所示:

Driver | Date         | Task       | Address 
Peter  | 02.05.2020   | Loading    | 12342, Berlin
Peter  | 03.05.2020   | Unloading  | 14221, Utrecht

有了这个不可更改的数据框,我现在可以像计划一样根据需要对其进行旋转。

m,n = df.shape
t_df = pd.DataFrame(columns=['driver', 'date', 'task', 'address'])
t_df['Driver'] = df['Driver'].tolist() * 2
t_df['Date'] = df['Date loading'].tolist() + df['Date unloading'].tolist()
t_df['Address'] = df['Loading Address'].tolist() + df['Unloading Address'].tolist()
t_df['Task'] = ['Loading'] * m + ['Unloading'] * m

现在,我将值task + address添加为一列。

t_df['Compound'] = t_df[['Task', 'Address']].agg(': '.join, axis=1)

concat_array = lambda x: '; '.join(x)

schedule = pd.crosstab(index=t_df['Driver'], columns=t_df['Date'], values=t_df['Compound'],
    aggfunc=concat_array)

我将获得以下数据框:

Date                02.05.2020  ...                05.05.2020
Driver                          ...                          
Franz                      NaN  ...  Unloading: 14231, Berlin
Peter   Loading: 12342, Berlin  ...                       NaN

现在,正如我在回答开始时所说的,假设同一天有多个加载和卸载操作,您需要某种任务标识符来匹配哪个任务属于哪个任务。您需要分配某种task_id,然后将其放在Compound列中。

注意:我使用'; '来分离任务,您可能想使用其他东西。

您可以在gist中检查完整的代码文件。

答案 1 :(得分:2)

我建议您利用Pandas的多索引功能来组织和排序数据。与其在“日期加载”,“日期卸载”,“加载地址”,“卸载地址”中没有单独的列,我将用“日期”的一列,“地址”的一列替换为新的列。列“正在加载”,因此我们可以更轻松地对数据进行排序。我还添加了一个delivery_id列,以使加载和卸载对保持匹配。因此,在第一步中,我只是将数据重组为更可排序的数据框:

data = [['Peter', '02.05.2020', '03.05.2020', '12342, Berlin', '14221, Utrecht'],
        ['Peter', '03.05.2020', '04.05.2020', '14221, Utrecht', '13222, Amsterdam'],
        ['Franz', '03.05.2020', '03.05.2020', '11111, Somewhere', '11221, Somewhere2'],
        ['Franz', '03.05.2020', '05.05.2020', '11223, Upsalla', '14231, Berlin']]

df = pd.DataFrame(data)
df = df.reset_index()
df.columns = ['Delivery_id', 'Driver', 'Date loading', 'Date unloading', 'Loading Address', 'Unloading Address']

df_loading = df[['Delivery_id', 'Driver', 'Date loading', 'Loading Address']]
df_loading['Loading'] = 'Loading'
df_loading.columns = ['Delivery_id', 'Driver', 'Date', 'Address', 'Loading']
df_unloading = df[['Delivery_id', 'Driver', 'Date unloading', 'Unloading Address']]
df_unloading['Loading'] = 'Unloading'
df_unloading.columns = ['Delivery_id', 'Driver', 'Date', 'Address', 'Loading']
df = pd.concat([df_loading, df_unloading])

下一步,将date列从字符串转换为datetime,以便Pandas将其理解为日期。

df['Date'] = pd.to_datetime(df['Date'], format='%d.%m.%Y')

然后就像将索引设置为我们要排序的值并对其进行排序一样简单:

df = df.set_index(['Driver', 'Date', 'Delivery_id', 'Loading']).sort_index()

输出:

print(df)

                                                   Address
Driver Date       Delivery_id Loading                     
Franz  2020-05-03 2           Loading     11111, Somewhere
                              Unloading  11221, Somewhere2
                  3           Loading       11223, Upsalla
       2020-05-05 3           Unloading      14231, Berlin
Peter  2020-05-02 0           Loading        12342, Berlin
       2020-05-03 0           Unloading     14221, Utrecht
                  1           Loading       14221, Utrecht
       2020-05-04 1           Unloading   13222, Amsterdam

转置输出,如果您更喜欢水平格式:

print(df.T.to_string())


Driver                  Franz                                                            Peter                                                  
Date               2020-05-03                                        2020-05-05     2020-05-02      2020-05-03                        2020-05-04
Delivery_id                 2                                  3              3              0               0               1                 1
Loading               Loading          Unloading         Loading      Unloading        Loading       Unloading         Loading         Unloading
Address      11111, Somewhere  11221, Somewhere2  11223, Upsalla  14231, Berlin  12342, Berlin  14221, Utrecht  14221, Utrecht  13222, Amsterdam

如果您希望按驱动程序保持垂直排序,而其余数据保持水平,则可以执行以下操作:

idx = pd.IndexSlice
for driver in df.T.columns.get_level_values(0).unique():
    print(df.loc[idx[driver, :, :]].T.to_string())
    print()

Driver                  Franz                                                  
Date               2020-05-03                                        2020-05-05
Delivery_id                 2                                  3              3
Loading               Loading          Unloading         Loading      Unloading
Address      11111, Somewhere  11221, Somewhere2  11223, Upsalla  14231, Berlin

Driver               Peter                                                  
Date            2020-05-02      2020-05-03                        2020-05-04
Delivery_id              0               0               1                 1
Loading            Loading       Unloading         Loading         Unloading
Address      12342, Berlin  14221, Utrecht  14221, Utrecht  13222, Amsterdam