中间合并/加入数据框以计算统计数据的性能问题

时间:2016-08-07 14:50:54

标签: python performance pandas merge

我有一个数据框,由来自约4000个机场的全部机场的城市机场组合对组成。组合的数量是数百万,但我使用的数据子集约为150万对(df_pairs行)。

df_pairs:

    city1   city2
    0   sfo yyz
    1   sfo yvr
    2   sfo dfw
    3   sfo ewr
    4   sfo pdx

输出df_pairs.to_dict('记录'):

[{'index': 0, 'city1': 'sfo', 'city2': 'yyz'},
 {'index': 1, 'city1': 'sfo', 'city2': 'yvr'},
 {'index': 2, 'city1': 'sfo', 'city2':'dfw'},
 {'index': 3, 'city1': 'sfo', 'city2':'ewr'},
 {'index': 4, 'city1': 'sfo', 'city2': 'pdx'}]

对于df_pairs中的每个城市对(行),我想执行各种对级别的计算。

我还有3个额外的数据框,其中包含有关每个机场的各种数字和分类信息。

它们看起来像(虽然有些dfs是月度数据和其他日常数据):

df_stats1:

city    fuel    landings    takeoffs    passengers
date                    
2014-05-01  sfo 2.32    4.26    4.87    6.58
2014-05-01  yyz 14.00   1.50    20.00   5.00
2014-05-01  yvr 24.78   2.90    50.55   6.64
2014-05-01  dfw 2.40    4.06    4.06    6.54
2014-05-01  ewr 30.35   9.96    64.24   6.66
2014-05-01  pdx 60.35   5.45    4.12    6.98

输出df_stats1.reset_index()。to_dict('记录'):

[{'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'sfo',
  'landings': 4.26,
  'passengers': 6.58,
  'fuel': 2.32,
  'takeoffs': 4.87},
 {'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'yyz',
  'landings': 1.5,
  'passengers': 5.00,
  'fuel': 14.00,
  'takeoffs': 20.00},
 {'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'yvr',
  'landings': 2.9,
  'passengers': 6.64,
  'fuel': 24.78,
  'takeoffs': 50.55},
 {'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'dfw',
  'landings': 4.06,
  'passengers': 6.54,
  'fuel': 2.4,
  'takeoffs': 4.06},
 {'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'ewr',
  'landings': 9.96,
  'passengers': 6.66,
  'fuel': 30.35,
  'takeoffs': 64.24},
  {'date': Timestamp('2014-05-01 00:00:00'),
  'city': 'pdx',
  'landings': 5.45,
  'passengers': 6.98,
  'fuel': 60.35,
  'takeoffs': 4.12}]

现在,我有一个由{/ p>执行的函数calstats

df_pairs.apply(calstats, axis=1, args=(v1,v2,v3,v4,v5,v6,v7, blah blah))

calcstats函数做的第一件事就是构造3个中间/临时数据帧,方法是从stat dfs中选择对中每个城市的数据,并通过执行{{1}将它们并排排列。 }}。

其中一个中间/临时dfs的示例:

merge

然后我使用3个中间/临时dfs(即tmp_city_pair_df)来执行各种计算,例如对之间的着陆差异,最大值(在所讨论的时间段内的这种差异),min()等。

我遇到了各种性能问题。

首先,构建3个中间dfs所需的总时间约为:city1_df = df_stats1[df_stats1['city'] == row['city1']] city2_df = df_stats1[df_stats1['city'] == row['city2']] tmp_city_pair_df = city1_df.merge(city2_df, left_index=True, right_index=True, how = 'right', suffixes=('_1','_2'))

我在0:00:00.048481中的行数约为1.5,因此执行中间dfs的总费用为df_pairs = 1,500,000 x 0:00:00.048481秒= 72,721.5小时。< / p>

因此构建中间dfs需要20.2小时,并且不包括在执行进一步计算时使用这些中间dfs所需的时间成本。

我想知道是否有更有效的方法来做到这一点。

基本上,我正在做的是在20city1city2中查找df_stats1df_stats2并构建中间/临时我可以使用dfs来执行对级计算。

更新

我想提供更多信息。

因此,目的是在一对基础上生成一个最终的数据帧,看起来如下所示,我可以用它进行进一步处理。

df_stats3

上面名为stat1到stat6的统计信息是原始数据中不存在的专有统计信息。

原始数据由3个数据帧组成,我称之为:

city1   city2   stat1, stat2, stat3, stat4, stat5, stat6 ...
    0   sfo yyz, x, x, x, x, x, x
    1   sfo yvr, y, y, y, y, y, y
    2   sfo dfw, z, z, z, z, z, z
    3   sfo ewr, a, a, a, a, a, a 
    4   sfo pdx, b, b, b, b, b, b

df_stat1 df_stat2 df_stat3 =过去24个月每个机场的每日数据(燃料,着陆,起飞,乘客)

df_stat1 = df_stat2但汇总到月份(通过df_stat1

df.stat1.groupby(['city',pd.TimeGrouper('M')] =过去24个月内每个机场的月度数据时间序列,包括着陆费,收入等信息

现在,要进入最终的数据帧,需要进行各种计算。我想计算像:

这样的东西
df_stat3

例如,在最终的数据框中,1) City1 Landings - City2 Landings (on a daily and monthly basis) 2) Sign of statistic in #1 (positive or negative) 可以是:

上述#2中唯一正值的总和。

因此,您可以看到需要进行各种操作才能到达最终的数据帧。

我不知道如何才能最好地利用pandas / python的矢量化功能。

例如,要生成上面#2中唯一正值的和,我需要为每个城市对加入每日数据时间序列(来自df_stat1),计算City1着陆与City2着陆之间的减法,然后求和积极的价值观。

1 个答案:

答案 0 :(得分:1)

Python(和pandas)在构建大量对象时表现不佳。 merge中每行的apply就是这样做的。相反,您可以尝试以下方法:

tmp = pd.merge(df_pairs, df_stats.add_suffix('_1'), left_on='city1', right_on='city_1', how='left')

pd.merge(tmp, df_stats.add_suffix('_2'), left_on='city2', right_on='city_2', how='left')

这将首先有效地执行合并(这里的双线结构是为了节省空间,并且仅在df_pairs中的所有对上进行合并。)

此外,您现在可以按矢量进行所有分析,在任何情况下都应该快得多。如果您添加有关所需分析的更多详细信息,则可以进一步解决此问题。

修改

根据对问题和评论的编辑,这里是处理日常数据的概述。具体来说,让我们处理登陆日期的每日差异(您可以适应各种变化,例如,只有正差异)。

假设您从

开始
landings_by_date = df_stats1[['city', 'date', 'landings']].set_index(['city', 'date']).unstack()
landings_by_date.columns = landings_by_date.columns.get_level_values(1)

要查找特定日期的着陆日期差异,例如第一个(索引0),您可以

lhs = pd.merge(df_pairs, landings_by_date.ix[:, [0]], left_on='city1', right_index=True, how='left').set_index(['city1', 'city2'])
rhs = pd.merge(df_pairs, landings_by_date.ix[:, [0]], left_on='city2', right_index=True, how='left').set_index(['city1', 'city2'])
lhs - rhs

(或者,为了下降到numpy,

(lhs - rhs).values

要计算所有日期的某些聚合,请在循环上执行此操作(以便日期索引为0,1,...),并更新聚合。

为什么这会更有效率?根据您问题的具体情况,每日日期约为3000,但约为1.5e6行。

  1. 即使你正在循环(这在数值Python中是不受欢迎的),你只需要进行约3000次迭代,并在每次迭代中进行矢量运算~1.5e6条目。

  2. 你不是在创建小型数据框架〜1.5e6次(如你的问题所示),你只创建(更大)数据框架约3000次。

  3. 内存需求应该很小 - 每个聚合额外增加约1.5e6。