在熊猫中获取日期分位数

时间:2016-04-28 21:51:15

标签: python numpy pandas

我有一些类似的数据:

user  timestamp  value1 
   a 2007-01-01       7 
   a 2007-02-02       8 
   a 2007-02-03       9 
   b 2007-02-04       1 
   a 2007-02-05       2 
   b 2007-02-06       3 
   b 2007-02-07       4 
   a 2007-02-08       5 
...

每个用户都有不同数量的条目。

我的目标是了解这些条目的生成速度,并输出如下内容:

     last_entry median_entry first_entry
user                                    
a    2007-02-08   2007-02-03  2007-01-01
b    2007-02-07   2007-02-06  2007-02-04

到目前为止,我的代码如下:

gb = df.groupby('user')
time_median = gb['timestamp'].median()

但这给了我DataError: No numeric types to aggregate,大概是因为日期不是数字。

我想可以将日期转换为时间戳,找到这些日期的中位数,然后将它们转回date_time对象。这是最好的方式吗?

6 个答案:

答案 0 :(得分:2)

我可能在我的问题中不清楚,但我找到了一个适合我的解决方案。

def get_quantile(df, q):
    # Function that gets quantile from integer timestamp, then changes
    # back to a date_time object
    return pd.to_datetime(df['timestamp'].quantile(q, interpolation='nearest'))

df = pd.DataFrame(data={'user': np.random.choice(['a', 'b','c'], size=100, replace=True), 'value': np.random.random(size=100), 'date_time': pd.date_range(start=date(2016, 1,1), freq='D', periods=100)})

# Make a column of integer timestamps
df['timestamp'] = df['date_time'].astype('int')

editors = d.groupby('editor')

result = pd.DataFrame()
# Add columns of quantiles
result['first_quantile'] = get_quantile(editors, .25)
etc.

答案 1 :(得分:1)

您可以使用.searchsorted()表示每位用户maxmin之间的天数:

df = pd.DataFrame(data={'user': np.random.choice(['a', 'b','c'], size=100, replace=True), 'value': np.random.random(size=100), 'time_stamp': pd.date_range(start=date(2016, 1,1), freq='D', periods=100)})

df.groupby('user')['time_stamp'].describe()

user        
a     count                      28
      unique                     28
      top       2016-02-03 00:00:00
      freq                        1
      first     2016-01-01 00:00:00
      last      2016-04-05 00:00:00
b     count                      38
      unique                     38
      top       2016-03-24 00:00:00
      freq                        1
      first     2016-01-02 00:00:00
      last      2016-04-08 00:00:00
c     count                      34
      unique                     34
      top       2016-01-28 00:00:00
      freq                        1
      first     2016-01-03 00:00:00
      last      2016-04-09 00:00:00

中位数:

df.groupby('user')['time_stamp'].apply(lambda x: x.sort_values().iloc[x.searchsorted(x.min() + (x.max()-x.min())/2)])

dtype: object
user    
a     54   2016-02-24
b     50   2016-02-20
c     51   2016-02-21
dtype: datetime64[ns]

答案 2 :(得分:1)

假设您希望将每个用户的初始日期之后的每个日期视为自此初始日期以来的天数,您可以执行类似

的操作
import pandas as pd
dts =  pd.date_range(start="2015-01-15", periods=20)
users = ["a","b"]*10
df = pd.DataFrame({"user":users, "timestamp":dts})

date_info = df.groupby("user").agg({"timestamp":[min, max]})
date_info.columns = date_info.columns.droplevel()

since_incept = lambda x: x - x.min()
df["days"] = df.groupby("user").transform(since_incept)
df["days"] = df["days"].dt.days

median_td = lambda x: pd.Timedelta(pd.Series.median(x), "D")
med = df.groupby("user").agg({"days":[median_td]})

date_info["median"] = date_info["min"] + med.loc[:, ("days", "<lambda>")]

答案 3 :(得分:1)

不确定这是否正是您所寻找的,但您可以尝试使用pd.TimeGrouper并更改频率('20D''M'等)以适应您的时间范围。以下是使用5分位数(100天,20天组)的示例:

示例数据:

df = pd.DataFrame({'user': np.random.choice(['a', 'b','c'], size=100, replace=True),
                   'value': np.random.randint(10, size=100),
                   'time_stamp': pd.date_range(start=pd.datetime(2016, 1,1), freq='D', periods=100)})
df.head()

  time_stamp user  value
0 2016-01-01    b      3
1 2016-01-02    c      4
2 2016-01-03    a      8
3 2016-01-04    b      5
4 2016-01-05    c      5    

分位数生成:

quantiles = df.set_index('time_stamp').groupby([pd.TimeGrouper(freq='20D'), 'user'])['value'].sum()

time_stamp  user
2016-01-01  a       48
            b       22
            c       29
2016-01-21  a       28
            b       26
            c       25
2016-02-10  a       20
            b       57
            c       26
2016-03-01  a       25
            b       37
            c       35
2016-03-21  a       15
            b       37
            c       22

对于累积视图:

cum_quantiles = quantiles.groupby(level=[1]).cumsum()

time_stamp  user
2016-01-01  a        48
            b        22
            c        29
2016-01-21  a        76
            b        48
            c        54
2016-02-10  a        96
            b       105
            c        80
2016-03-01  a       121
            b       142
            c       115
2016-03-21  a       136
            b       179
            c       137

如果要查看百分比值,请尝试添加百分比列:

totals = df.groupby('user')['value'].sum()
df['pct'] = df.apply(lambda x: x['value']/float(totals[x['user']]), axis=1)

重复上述步骤,将'value'更改为'pct'

答案 4 :(得分:1)

使用我们自己的中位函数分组

设置列

sapply(cn, function(x) rowSums(mydata[, paste0(x, 1:2)]))

定义我们自己的时间中值函数

df['first_entry'] = df['timestamp']
df['median_entry'] = df['timestamp']
df['last_entry'] = df['timestamp']

设置聚合配置

def median_time(x):
    x = list(x)
    median_entry = (len(x) - 1) / 2.0
    x.sort()
    if median_entry % 1 == 0:
        return x[int(median_entry)]
    else:
        lower_date = x[int(median_entry)]
        upper_date = x[int(median_entry) + 1]
        return lower_date + (upper_date - lower_date) / 2.0

汇总

agg_config = {'first_entry': pd.np.min,
              'median_entry': median_time,
              'last_entry': pd.np.max}

结果

df.groupby('user').agg(agg_config)

替代,更简单的中位数

如果您只想要整个日期,也可以通过这种方式设置错误的中位数:

     last_entry median_entry first_entry
user                                    
a    2007-02-08   2007-02-03  2007-01-01
b    2007-02-07   2007-02-06  2007-02-04

答案 5 :(得分:1)

如果您不需要精确的中位数,您可以对日期进行排序并采用近似中间值(例如偶数个元素的中位数将是元组对中的第一个数字,因此1, 2, 2, 4, 4, 6的中位数将是2因为(2,4)是中间元素。)

>>> df.groupby('user').timestamp.agg({
        'first_entry': 'first', 
        'last_entry': 'last', 
        'median_entry': lambda group: sorted(group)[len(group) // 2]})

      last_entry first_entry median_entry
user                                     
a     2007-02-08  2007-01-01   2007-02-03
b     2007-02-07  2007-02-04   2007-02-06