Pandas python:按月和项目状态计算的服务数量

时间:2014-12-12 13:28:47

标签: python pandas

我有这个测试数据集,显示每个客户端,service_id和一段时间的服务状态('in_progress'或'stopped')。我编写了代码,因此您可以复制并粘贴以生成DataFrame。让我们看看(暂时忽略左箭头):

In [1]: import pandas as pd
In [2]: my_data = \
      [{'client_id' : '01', 'service_id': '01', 'status_start' : '2014-01-01', 'status_end' : '2014-02-13', 'service_status' : 'in_progress'},
       {'client_id' : '01', 'service_id': '02', 'status_start' : '2014-01-01', 'status_end' : '2014-02-18', 'service_status' : 'stopped'},
       {'client_id' : '01', 'service_id': '12', 'status_start' : '2014-02-14', 'status_end' : '2014-04-13', 'service_status' : 'in_progress'},
       {'client_id' : '02', 'service_id': '56', 'status_start' : '2014-03-01', 'status_end' : '2014-04-13', 'service_status' : 'in_progress'},
       {'client_id' : '02', 'service_id': '58', 'status_start' : '2014-02-04', 'status_end' : '2014-04-13', 'service_status' : 'stopped'},
       {'client_id' : '02', 'service_id': '60', 'status_start' : '2014-02-08', 'status_end' : '2014-04-23', 'service_status' : 'stopped'},
       {'client_id' : '03', 'service_id': '61', 'status_start' : '2014-02-10', 'status_end' : '2014-04-28', 'service_status' : 'in_progress'},
       {'client_id' : '03', 'service_id': '63', 'status_start' : '2014-02-01', 'status_end' : '2014-04-28', 'service_status' : 'in_progress'},
       {'client_id' : '03', 'service_id': '65', 'status_start' : '2014-01-10', 'status_end' : '2014-03-28', 'service_status' : 'in_progress'}
       ]
In [3]: df = pd.DataFrame(my_data)
In [4]: df

          client_id service_id  status_start    status_end  service_status
-->  0    01        01          2014-01-01      2014-02-13  in_progress
-->  1    01        02          2014-01-01      2014-02-18  stopped
-->  2    01        12          2014-02-14      2014-04-13  in_progress
     3    02        56          2014-03-01      2014-04-13  in_progress
     4    02        58          2014-02-04      2014-04-13  stopped
     5    02        60          2014-02-08      2014-04-23  stopped
     6    03        61          2014-02-10      2014-04-28  in_progress
     7    03        63          2014-02-01      2014-04-28  in_progress
-->  8    03        65          2014-01-10      2014-03-28  in_progress

我想问这些数据的问题是:每个service_status中有多少服务,每月和每个客户?

也就是说,例如,1月份的客户'01'有1个服务'in_progress'和1'停止'。同一个客户'01',在二月有2'in_progress'(已经在1月份和2月份新的那个)和1个新标记为'已停止'。但是在3月和4月只有一个服务'in_progress'(services_ids'01'和'02'在2月终止)。按照同样的规则,1月份的客户'03'有1个服务'in_progress',0'停止'。

所以最终的DataFrame看起来像这样(现在你看到箭头突出显示刚才注释的例子的行):

In [5]: summary_df

           client_id    month   status_in_progress  status_stopped
-->    0    01          Jan     1                   1
-->    1    01          Feb     2                   1
-->    2    01          Mar     1                   0
-->    3    01          Apr     1                   0
       4    02          Jan     0                   0
       5    02          Feb     0                   2
       6    02          Mar     1                   2
       7    02          Apr     1                   2
-->    8    03          Jan     1                   0
       9    03          Feb     3                   0
       10   03          Mar     3                   0
       11   03          Apr     2                   0

我尝试使用groupbypivot_table,但我没有成功。好吧,我必须诚实:我使用了一个for循环,需要6个小时才能完成(原始数据集的行数超过500万)。

有人可以帮忙吗?熊猫/蟒蛇新手,请耐心等待! :)

谢谢!

1 个答案:

答案 0 :(得分:0)

您必须创建一个每月有一条记录的新数据集,例如为此记录:

          client_id service_id  status_start    status_end  service_status
-->  0    01        01          2014-01-01      2014-02-13  in_progress

我们在新数据集中创建这两个记录:

          client_id service_id  month    service_status
          01        01          2014-01  in_progress
          01        01          2014-02  in_progress

然后按client_id,month和service_status进行分组。

def month_id(s):
  """Convert YYYY-MM-DD to a month id"""
  y = int( s[0:4] )
  m = int( s[5:7] )
  return y*12 + m

def to_yyyymm(mid):
  """Convert a month id to YYYY-MM"""
  y = mid / 12
  m = mid % 12
  return "%04d-%02d" %  (y, m)

# Convert my_data to one record per month.

new_data = []
for r in my_data:
  mstart = month_id(r['status_start'])
  mend = month_id(r['status_end'])
  for mid in range(mstart, mend+1):
    m = to_yyyymm(mid)
    new_data.append( { 'client_id': r['client_id'], 'service_id': r['service_id'], 'service_status': r['service_status'], 'month': m } )

df = pd.DataFrame(new_data)
grouped = df.groupby(['client_id', 'month', 'service_status'])
for name, g in grouped:
  print name, len(g)

输出:

('01', '2014-01', 'in_progress') 1
('01', '2014-01', 'stopped') 1
('01', '2014-02', 'in_progress') 2
('01', '2014-02', 'stopped') 1
('01', '2014-03', 'in_progress') 1
('01', '2014-04', 'in_progress') 1
('02', '2014-02', 'stopped') 2
('02', '2014-03', 'in_progress') 1
('02', '2014-03', 'stopped') 2
('02', '2014-04', 'in_progress') 1
('02', '2014-04', 'stopped') 2
('03', '2014-01', 'in_progress') 1
('03', '2014-02', 'in_progress') 3
('03', '2014-03', 'in_progress') 3
('03', '2014-04', 'in_progress') 2

特定状态的缺失记录表示该客户端和月份的计数为零。

如果数据在数据框中,请使用df.itertuples()迭代行:

i_client_id = 1+df.columns.get_loc('client_id')
i_service_id = 1+df.columns.get_loc('service_id')
...
for r in df.itertuples():
  ... same code except replace r['client_id'] with r[i_client_id], etc.