我在MySQL数据库中有一个大型音乐标记数据集,我试图用pandas进行分析。我将它从MySQL导出到.tsv,现在将其作为数据框读取进行分析。
数据中的每一行都是一个元组,表示给定用户(由数字用户ID表示)在特定时间用特定标记(此处表示为数字ID)标记特定艺术家。因此,如果没有索引,数据样本将如下所示:
uid artist tag date
0 2096963 559 46 2005-07-01
1 2096963 584 1053 2005-07-01
2 2096963 584 2044 2005-07-01
3 2096963 584 2713 2005-07-01
4 2096963 596 236 2005-07-01
...
uid artist tag date
99995 2656262 8095 57 2005-08-01
99996 2656262 8095 79 2005-08-01
99997 2656262 8095 4049 2005-08-01
99998 2656262 8095 8290 2005-08-01
99999 2610168 8095 1054 2005-08-01
为方便分析,我已将所有内容编入索引并添加了虚拟注释变量(数据中的每一行代表一个标记实例或注释)。所以现在我们有:
data = pd.read_table(filename,header=None, names=('uid','artist','tag','date'), index_col=['date','uid','artist','tag'], parse_dates='date')
data['annotations'] = 1
In [41]: data.head()
Out[41]:
annotations
date uid artist tag
2005-07-01 2096963 559 46 1
584 1053 1
2044 1
2713 1
596 236 1
...
使用这样的格式化数据,计算简单的频率分布是微不足道的。例如,如果我想确定每个用户标记某些内容的次数(以降序频率顺序排列),那就简单如下:
data.sum(level='uid').sort('anno',ascending=False)
同样,我可以通过以下方式确定每个月(所有用户和标签)的注释总数:
data.sum(level='date')
但是我遇到了更复杂的计算问题。特别是,如果我想每个月每个用户的平均注释数量会怎么样?如果我打电话:
data.sum(level=['date','uid']).head()
我每个月都得到每个用户的注释数量,即:
anno
date uid
2005-07-01 1040740 10
1067454 23
2096963 136
2115894 1
2163842 4
...
但是,然后通过简单的方法获得跨用户的月平均值?也就是说,对于每个月,“anno”列的用户平均值是多少?我有各种各样的指标,我想计算,所以我希望解决方案能够概括。
答案 0 :(得分:2)
Big MultiIndexes可能很麻烦。我建议放弃您的虚拟列,“注释”,并使用count
代替sum
。
首先,读入数据而不指定索引,即
pd.read_table(filename,header=None, names=['uid','artist','tag','date'], parse_dates='date')
计算每个用户的注释:
data.groupby('uid').count().sort(ascending=False)
每天总注释:
data.groupby('date').count()
每天统计唯一身份用户:
daily_users = data.groupby('date').uid.nunique()
每天总注释:
daily_annotations = data.groupby('date').count()
每个用户的平均每日注释只是每日总注释除以当天的用户数。 作为groupby
操作的结果,这两个系列都按日期编制索引,因此它们将自动对齐。
mean_daily_annotations_per_user = daily_annotations/daily_users
要在每个用户之间平均注释,最方便的是使用resample
,这是一个很好的功能,可以按不同的时间频率进行分组。
mean_monthly_annotations_per_user = mean_daily_anootations_per_user.resample('M')
答案 1 :(得分:1)
我找到了一种适合我原来的多索引格式的替代方法,而且认为比@DanAllan提出的方法更快。
回想一下,我们计算每个用户每月的平均注释,让我们构建两个数据帧(我这里只使用了一部分数据,因此是nrows参数)。 data1是带有虚拟变量的多索引版本,data2是@DanAllan提出的无索引版本
indexes=['date','uid','artist','iid','tag']
data1 = pd.read_table(filename,header=None, nrows=1000000, names=('uid','iid','artist','tag','date'),index_col=indexes, parse_dates='date')
data['anno']=1
data2 = pd.read_table(filename,header=None, nrows=1000000, names=('uid','iid','artist','tag','date'), parse_dates='date')
使用未索引的(data2)版本,该过程为:
daily_users = data2.groupby('date').uid.nunique()
daily_annotations = data2.groupby('date').count().uid
anno_per_user_perday2 = daily_annotations / daily_users.map(float)
使用多索引版本(data1),我们可以:
anno_per_user_perday = data1.sum(level=['date','uid']).mean(level='date').anno
结果完全相同,但索引版本的速度快了两倍(对于完整的5000万行数据集,性能将更加严重):
%timeit -n100 daily_users = data2.groupby('date').uid.nunique() ; daily_annotations = data2.groupby('date').count().uid ; anno_per_user_perday2 = daily_annotations / daily_users.map(float)
100 loops, best of 3: 387 ms per loop
%timeit -n100 anno_per_user_perday1 = data1.sum(level=['date','uid']).mean(level='date').anno
100 loops, best of 3: 149 ms per loop
使用索引版本生成数据帧的速度较慢,但它提供的灵活性似乎值得。