【利用python进行数据分析】准备与实例(二)

MovieLens 1M数据集

GroupLens Research采集了一组从20世纪90年末到21世纪初由MovieLens用户提供的电影评分数据。MovieLens 1M数据集含有来自6000名用户对4000部电影的100万条评分数据。它分为三个表:评分、用户信息和电影信息。

我们来试着读取数据,按性别计算每部电影的平均分

import pandas as pd
encoding = 'latin1'

unames = ['user_id', 'gender', 'age', 'occupation', 'zip']
rnames = ['user_id', 'movie_id', 'rating', 'timestamp']
mnames = ['movie_id', 'title', 'genres']

users = pd.read_csv('ch02/movielens/users.dat', sep='::', header=None, names=unames, encoding=encoding, engine = 'python')
ratings = pd.read_csv('ch02/movielens/ratings.dat', sep='::', header=None, names=rnames, encoding=encoding, engine = 'python')
movies = pd.read_csv('ch02/movielens/movies.dat', sep='::', header=None, names=mnames, encoding=encoding, engine = 'python')

data=pd.merge(pd.merge(ratings,users),movies)
mean_ratings= data.pivot_table('rating',index='title',columns='gender',aggfunc='mean')
print(mean_ratings[:5])

输出结果:

gender                                F         M
title                                            
$1,000,000 Duck (1971)         3.375000  2.761905
'Night Mother (1986)           3.388889  3.352941
'Til There Was You (1997)      2.675676  2.733333
'burbs, The (1989)             2.793478  2.962085
...And Justice for All (1979)  3.828571  3.689024

该操作产生了另一个DataFrame,其内容为电影平均得分,行标为电影名称,列标为性别。现在,我打算过滤掉评分数据不够250条的电影。为了达到这个目的,我先对title进行分组,然后利用size()得到一个含有各电影分组大小的Series对象:

ratings_by_title=data.groupby('title').size()
active_title=ratings_by_title.index[ratings_by_title>=250]
mean_ratings=mean_ratings.ix[active_title]
print(mean_ratings[:5])

输出结果:

gender                                    F         M
title                                                
'burbs, The (1989)                 2.793478  2.962085
10 Things I Hate About You (1999)  3.646552  3.311966
101 Dalmatians (1961)              3.791444  3.500000
101 Dalmatians (1996)              3.240000  2.911215
12 Angry Men (1957)                4.184397  4.328421

为了了解女性观众最喜欢的电影,我们可以对F列降序排列:

top_female_ratings = mean_ratings.sort_values(by='F', ascending=False)
print(top_female_ratings[:5])

输出结果:

gender                                                     F         M
title                                                                 
Close Shave, A (1995)                               4.644444  4.473795
Wrong Trousers, The (1993)                          4.588235  4.478261
Sunset Blvd. (a.k.a. Sunset Boulevard) (1950)       4.572650  4.464589
Wallace & Gromit: The Best of Aardman Animation...  4.563107  4.385075
Schindler's List (1993)                             4.562602  4.491415

假设我们想要找出男性和女性观众分歧最大的电影。一个办法是给mean_ratings加上一个用于存放平均得分之差的列diff,并对其进行排序,按“diff”升序排序即可得到分歧最大且为女性观众更喜欢的电影:

mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F']
sorted_by_diff = mean_ratings.sort_values(by='diff')
print(sorted_by_diff[:5])

输出结果:

gender                            F         M      diff
title                                                  
Dirty Dancing (1987)       3.790378  2.959596 -0.830782
Jumpin' Jack Flash (1986)  3.254717  2.578358 -0.676359
Grease (1978)              3.975265  3.367041 -0.608224
Little Women (1994)        3.870588  3.321739 -0.548849
Steel Magnolias (1989)     3.901734  3.365957 -0.535777

如果只是想要找出分歧最大的电影(不考虑性别因素),则可以计算得分数据的方差或标准差

#根据电影名称分组的得分数据的标准差
rating_std_by_title = data.groupby('title')['rating'].std()
#根据active_titles进行过滤
rating_std_by_title = rating_std_by_title.ix[active_titles]
#根据值对Series进行降序排列
print(rating_std_by_title.sort_values(ascending=False)[:5])

输出结果:

title
Dumb & Dumber (1994)                     1.321333
Blair Witch Project, The (1999)          1.316368
Natural Born Killers (1994)              1.307198
Tank Girl (1995)                         1.277695
Rocky Horror Picture Show, The (1975)    1.260177
Name: rating, dtype: float64

可能你已经注意到了,电影分类是以竖线(|)分隔的字符串形式给出的。如果想对电影分类进行分析的话,就需要先将其转换成更有用的形式才行。我将在本书后续章节中讲到这种转换处理,到时还会再用到这个数据。

1880-2010年间全美婴儿姓名

 美国社会保障总署(SSA)提供了一份从1880年到2010年的婴儿名字频率数据。

import pandas as pd
names1880 = pd.read_csv('ch02/names/yob1880.txt', names=['name', 'sex', 'births'])
print(names1880[:5])

输出结果:

        name sex  births
0       Mary   F    7065
1       Anna   F    2604
2       Emma   F    2003
3  Elizabeth   F    1939
4     Minnie   F    1746

这些文件中仅含有当年出现超过5次的名字。为了简单起见,我们可以用births列的sex分组小计表示该年度的births总计

print(names1880.groupby('sex').births.sum())

输出结果:

sex
F     90993
M    110493
Name: births, dtype: int64

由于该数据集按年度被分隔成了多个文件,所以第一件事情就是要将所有数据都组装到一个DataFrame里面,并加上一个year字段。使用pandas.concat即可达到这个目的:

# 2010是目前最后一个有效统计年度
years = range(1880, 2011)

pieces = []
columns = ['name', 'sex', 'births']

for year in years:
    path = 'ch02/names/yob%d.txt' % year
    frame = pd.read_csv(path, names=columns)

    frame['year'] = year
    pieces.append(frame)

# 将所有数据整合到单个DataFrame中
names = pd.concat(pieces, ignore_index=True)

输出结果:

       name sex  births  year
0  Isabella   F   22731  2010
1    Sophia   F   20477  2010
2      Emma   F   17179  2010
3    Olivia   F   16860  2010
4       Ava   F   15300  2010

有了这些数据之后,我们就可以利用groupby或pivot_table在year和sex级别上对其进行聚合

total_births = names.pivot_table('births', index='year',columns='sex', aggfunc=sum)
print(total_births.tail())

输出结果:

sex         F        M
year                  
2006  1896468  2050234
2007  1916888  2069242
2008  1883645  2032310
2009  1827643  1973359
2010  1759010  1898382

还可以画出图像:

total_births.plot(title='Total births by sex and year')
plt.show()

下面我们来插入一个prop列,用于存放指定名字的婴儿数相对于总出生数的比例。prop值为0.02表示每100名婴儿中有2名取了当前这个名字。因此,我们先按year和sex分组,然后再将新列加到各个分组上:

def add_prop(group):
    births = group.births.astype(float)
    group['prop'] = births / births.sum()
    return group
names = names.groupby(['year', 'sex']).apply(add_prop)

输出结果:

        name sex  births  year      prop
0       Mary   F    7065  1880  0.077643
1       Anna   F    2604  1880  0.028618
2       Emma   F    2003  1880  0.022013
3  Elizabeth   F    1939  1880  0.021309
4     Minnie   F    1746  1880  0.019188

 

你可能感兴趣的:(python,读书笔记,code实验室)