根据Python / Pandas

时间:2015-08-22 00:27:59

标签: python datetime pandas

所以我有一个用水数据集,其中包含我想要操作的3个关键列,如下所示:

   meter_read_date  meternumber  consumption
0        1/25/2011            1         4320
1        2/22/2011            1         3800
2        3/28/2011            1         4440
3        4/29/2011            1         4440
4         6/6/2011            1         5320
5         6/9/2011            1        20214
6        7/30/2011            1        20214
7        8/30/2011            1         2952
8        9/30/2011            1         3684
9       10/29/2011            1         3374
10      11/27/2011            1         3866
11      12/27/2011            1         3470
12       1/25/2011            3         5900
13       2/22/2011            3         5720
14       3/28/2011            3         7320
15       4/28/2011            3         6360
16        6/6/2011            3         9420
17        7/5/2011            3         8120
18        8/2/2011            3         8520
19        9/3/2011            3         6740
20      10/10/2011            3         6820
21       11/7/2011            3         5720
22       12/1/2011            3         3940
23      12/29/2011            3         6260

我想按日历月计算每个米数的使用情况,我认为这涉及到平均每日价值,然后将总和汇总到月度范围。

澄清一下,第1行的消耗量为3800.这意味着2011年2月22日消费量为3800,自2011年1月25日之前的测量结果以来,我们知道3800加仑的用水量已经发生从2011年1月26日至2011年2月22日。我想知道日历月的消费,所以我会这样做:

2011年1月26日消费 - 2011年2月22日= 28天内3800加仑= ~146.2加仑/天

消费从2011年2月23日 - 2011年3月28日= 4440加仑超过34天= ~130.6加仑/天

因此,估计消耗量从2/1 - 2/28 = 146.2加仑/天和2 / 1-2 / 22和130.6加仑/天从2 / 23-2 / 28 = 3216.4 + 783.6 = 4000加仑日历月

我还没有能够想到一种有效的方法来做到这一点而没有嵌套循环,我真的不会在100万大小的数据集上使用它。 有什么想法吗?

如果有任何其他信息我可以发帖给我,请告诉我。

3 个答案:

答案 0 :(得分:1)

这应该做你想要的大部分,虽然它不会是最有效的内存方式。我将在下面详细说明。

第一步是弄清楚每日消费。

df.meter_read_date = pd.to_datetime(df.meter_read_date)
df['days_since'] = df.groupby('meternumber').meter_read_date.diff()
df = df.set_index('meter_read_date')
df['daily_consumption'] = df.consumption / df.days_since.dt.days

现在,第一个仪表的数据框看起来像这样。

                 meternumber  consumption  days_since  daily_consumption
meter_read_date                                                         
2011-01-25                 1         4320         NaT                NaN
2011-02-22                 1         3800     28 days         135.714286
2011-03-28                 1         4440     34 days         130.588235
2011-04-29                 1         4440     32 days         138.750000
2011-06-06                 1         5320     38 days         140.000000
2011-06-09                 1        20214      3 days        6738.000000
2011-07-30                 1        20214     51 days         396.352941
2011-08-30                 1         2952     31 days          95.225806
2011-09-30                 1         3684     31 days         118.838710
2011-10-29                 1         3374     29 days         116.344828
2011-11-27                 1         3866     29 days         133.310345
2011-12-27                 1         3470     30 days         115.666667

此时至少有两种方法可以继续。最节省内存的方法是计算每组消费的比例,但这远非直截了当,尤其是因为您的数据每月可能有多于或少于一个读数。所以这种方式是(1)可能的,(2)记忆效率高,(3)相对较难。

另一种方式更容易,并且使用resample转换为每日观察并回填每日消费。

df_daily = df.groupby('meternumber')['daily_consumption'].resample(
                                        '1d',fill_method='bfill').reset_index()

这里的缺点是我们每天都有观察,对记忆施加更多压力,但它会自动解决很多问题 - 无需担心每个月有多少天或者每个月有多少读数。这里有几行,以其中一个读数为中心。

    meternumber meter_read_date           0
27            1      2011-02-21  135.714286
28            1      2011-02-22  135.714286
29            1      2011-02-23  130.588235
30            1      2011-02-24  130.588235

从那里,你所要做的就是聚合。 (请注意,第一个月和最后一个月基于部分数据,您可能希望放弃它们或按比例计算每日消费量。)

df_daily['month'] = df_daily.meter_read_date.dt.month
df_daily.reset_index().groupby(['meternumber','month'])[0].sum()

meternumber  month
1            1          950.000000
             2         3769.243697
             3         4072.720588
             4         4163.750000
             5         4340.000000
             6        29377.411765
             7        11985.814042
             8         2975.612903
             9         3565.161290
             10        3640.620690
             11        3946.379310
             12        3123.000000
3            1         1430.000000
             2         5786.050420
             3         6643.719165
             4         6227.593052
             5         7487.692308
             6         8169.230769
             7         9311.428571
             8         6716.696429
             9         5608.631757
             10        6133.243243
             11        5205.833333
             12        6424.166667

关于替代方法的一些简短想法:如果上述原因引起记忆问题,我认为可能存在混合方法。基本上,在计算每日消费量后,通过添加每个月的第一天和最后一天进行部分重新采样。从那里你可以以类似的方式聚合,虽然你需要基本上做加权和而不是简单的总和。

编码仍然比上述方法更难,但在内存上会更容易。尽管如此,这样做应该会大大简化编码,因为每一行现在都属于特定的月份,你不必尝试在不同月份分割读数。

答案 1 :(得分:0)

假设您的数据框名称为df,而meter_read_date是字符串/对象类型。由于数据框有一个日期列,并且您希望按月进行一些计算,因此最好将日期列转换为日期时间(如果不是)并将列设置为索引。

from dateutil.parser import parse
#  convert meter_read_date to datetime
df['meter_read_date'] = df.meter_read_date.apply(parse)
df = df.set_index(['meter_read_date'])

#  so you can group by year month and do some calculations
#  a datetime index have a convenient way to get its element:year,month,day, etc 
df.groupby([df.index.year,df.index.month,'meternumber'])['consumption'].mean()

# the result is :
      meternumber
2011  1   1               4320
          3               5900
      2   1               3800
          3               5720
      3   1               4440
          3               7320
      4   1               4440
          3               6360
      6   1              12767
          3               9420
      7   1              20214
          3               8120
      8   1               2952
          3               8520
      9   1               3684
          3               6740
      10  1               3374
          3               6820
      11  1               3866
          3               5720
      12  1               3470
          3               5100
Name: consumption, dtype: int64

答案 2 :(得分:0)

首先,让我们在指示月份添加一列。如果您的日期列是字符串,请使用方法A.如果是日期时间或时间戳,请使用方法B.

方法A:

df['month'] = df.meter_read_date.apply(lambda date_str: date_str.split("/")[0])

方法B:

df['month'] = pd.to_datetime(df.meter_read_date).apply(lambda date: date.month)

现在,您只需要在仪表ID和新创建的月份上执行groupby

gb = pd.DataFrame(df.groupby(['meternumber', 'month']).consumption.mean())
>>> gb
                       consumption
meternumber month             
1           1             4320
            2             3800
            3             4440
            4             4440
            6            12767
            7            20214
            8             2952
            9             3684
            10            3374
            11            3866
            12            3470
3           1             5900
            2             5720
            3             7320
            4             6360
            6             9420
            7             8120
            8             8520
            9             6740
            10            6820
            11            5720
            12            5100

您还可以查看相对于所有仪表的此数据,以帮助识别具有高于平均值的那些仪表。用法:

gb['monthly_avg'] = gb.reset_index().groupby('month').consumption.transform('mean').values
gb['relative_usage'] = gb.consumption / gb.monthly_avg

>>> gb
                   consumption  monthly_avg  relative_usage
meternumber month                                          
1           1             4320       5110.0        0.845401
            2             3800       4760.0        0.798319
            3             4440       5880.0        0.755102
            4             4440       5400.0        0.822222
            6            12767      11093.5        1.150854
            7            20214      14167.0        1.426837
            8             2952       5736.0        0.514644
            9             3684       5212.0        0.706830
            10            3374       5097.0        0.661958
            11            3866       4793.0        0.806593
            12            3470       4285.0        0.809802
3           1             5900       5110.0        1.154599
            2             5720       4760.0        1.201681
            3             7320       5880.0        1.244898
            4             6360       5400.0        1.177778
            6             9420      11093.5        0.849146
            7             8120      14167.0        0.573163
            8             8520       5736.0        1.485356
            9             6740       5212.0        1.293170
            10            6820       5097.0        1.338042
            11            5720       4793.0        1.193407
            12            5100       4285.0        1.190198