2019-04-08 14:33:02 qq_38038143 阅读数 199
  • Python数据分析实战-Pandas

    深度学习、机器学习和数据分析必须用pandas。pandas是在python直接流行的数据处理框架。可以说,如果不会使用pandas,就谈不上会用python做数据分析。本课程会使用奥林匹克一个真实的数据作为实验数据,从初级到各种pandas的常用操作,到常用的数据可视化,让你在短的时间内掌握好pandas,轻松愉快的玩转数据分析。

    2322 人正在学习 去看看 阿勒拉哈

前言

数据集介绍:
1931年和1932年,明尼苏达州收集了圣保罗大学农场(University Farm, St. Paul) 1/40英亩土地上种植的10个大麦品种的每英亩产量蒲式耳(bushels per英亩)数据,以及位于瓦塞卡(Waseca)、莫里斯(Morris)、克罗斯顿(Crookston)、大瀑布(Grand Rapids)和德卢斯(Duluth)的5个分站(均位于明尼苏达州)的产量数据。这些品种在1931年和1932年分别在六个试验站的三个随机区种植,每年测试使用不同的土地。

环境:
Python-3.7 + NumPy + Matplotlib + Pandas + JupyterNotebook

下载:数据集 – barley2.csv

1. 正文

  1. 题目解析
    此次数据集为1931年和1932年,明尼苏达州收集了圣保罗大学农场上种植的10个大麦品种的在6个试验站的产量数据。
    因此,最终我们需要分析出那个大麦相对于其他品种的大麦产量最高。每个试验站种植哪类大麦的产量是最高的。并且,最终需要给出各地区最佳种植的大麦品种。

2.使用工具
使用Python语言分析,包含Numpay、Matplotlib和Pandas库。
3.分析
首先,给出的数据集是经过清洗的,不存在异常值和缺失值。
先分析10种大麦产量,这里分析了各品种大麦均值、各品种大麦的标准差和所有大麦的整体均值:
在这里插入图片描述
图1
从图1可以看出,所有大麦的整体均值约为34。有5种大麦的平均值能够达到整体平均值及以上。另外,Trebi和Wisconsin No. 38的产量均值最高的,Svansota的均值产量最低。从标准差看出,品种No. 462产量最不稳定,Peatland产量最稳定。
然后我们分析各地区的产量情况:
在这里插入图片描述
图2
从图2看出,试验站Waseca大麦总体产量最高,Grand Rapids产量最低。进一步按年份划分,1931年Waseca总体产量最高,32年Grand Rapids产量最低。
再从大麦品种分析:
在这里插入图片描述
图3
从图3看出,品种Trebi总产量最高,Svansota总产量最低。
从1931年和1932年分析:
在这里插入图片描述
图4
从上图可以看出1931年各品种大麦产量比1932年产量高。可以推测这两年的天气差异可能存在很大的差别,并且大麦产量会受到天气的影响。
从地区和大麦品种角度分析:
在这里插入图片描述
图5
从图5可以看出,试验站Waseca和大麦品种Wisconsin No. 38的组合产量最高,试验站Grand Rapids和品种No. 475组合产量最低。
为更加清晰的看出10种大麦在6个试验站的产量情况,绘制如下雷达图:
在这里插入图片描述
在这里插入图片描述
(这里只展示2张)

最终,计算各地区产量的四分位数,按照上四分位数将大麦归为Ⅰ级,下四分位数为Ⅲ级,其余为Ⅱ级。并对各地区的产量做极值标准化。给出各地区大麦品种产量级别表:
在这里插入图片描述

2. 代码:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os

# 读取数据
data = pd.read_csv('./barley2.csv')
# 设置中文显示
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False

# 取出试验点名、种类名
sites = data.Site.unique()
varieties = data.Variety.unique()

def mkdir():
    """创建保存图片的目录"""
    path = os.path.abspath(os.curdir) + '\\pictures_png'
    if os.path.exists(path) == False:
        os.mkdir(path)
    # 创建雷达图目录
    path = os.path.abspath(os.curdir) + '\\pictures_png\\radar'
    if os.path.exists(path) == False:
        os.mkdir(path)


def save_picture(plt, picture_name, title=None, xlabel=None, ylabel=None):
    """保存图片函数"""
    path = "./pictures_png/"
    plt.title(title)
    if xlabel:
        plt.xlabel(xlabel)
    if ylabel:
        plt.ylabel(ylabel)
    plt.tight_layout()  # 避免图片保存不完整
    plt.savefig(path + picture_name, dpi=120)
    plt.show()


def mean_std():
    """计算各种类平均值、总体平均值、各种类标准差"""
    # 种类平均值
    varieties_mean = data.groupby('Variety')['Yield'].mean()
    # 总体均值
    all_mean = data.Yield.mean()
    # 标准差
    varieties_standard_deviation = data.groupby('Variety')['Yield'].std()

    fig,ax1 = plt.subplots()
    # 均值
    plt.plot(varieties, [all_mean for i in range(len(varieties))])

    # 种类均值
    plt.bar(varieties, varieties_mean, facecolor='yellowgreen', width=0.5)
    plt.grid(axis='y')
    plt.xticks(rotation=40)
    plt.xlabel("Variety")
    plt.ylabel("Yield")
    plt.yticks(range(0,50,5))

    # 标准差
    ax2 = ax1.twinx()
    plt.plot(varieties, varieties_standard_deviation,'.-', lw=2, color='r')
    plt.yticks(range(7,15))
    plt.legend('标准差', loc = 'best')
    plt.yticks(range(0,15))
    save_picture(plt, "整体分析.png", title="各类别产量均值、总体均值和标准差")
    

def analysis_by_site():
    """按地区分析"""
    fig, axes = plt.subplots(2,1)

    # 按地区绘制总产量
    data.groupby(['Site'])['Yield'] \
        .sum() \
        .sort_values() \
        .plot(kind='barh', figsize=(8,4), ax=axes[0])

    # 按地区、年份绘制总产量
    data.groupby(['Year', 'Site'])['Yield'] \
        .sum() \
        .sort_values() \
        .plot(kind='barh', figsize=(8,8), ax=axes[1])
    save_picture(plt, "各实验站每年总产量.png", xlabel="Yield")


def analysis_by_variety():
    """按大麦类别"""
    # 按大麦种类
    data.groupby('Variety')['Yield'] \
        .sum() \
        .sort_values() \
        .plot(kind='barh', figsize=(8,4))
    save_picture(plt, "各种类大麦总产量.png", title="各种类大麦总产量", xlabel="Yield")

def analysis_by_year():
    """按年份"""
    data.groupby( 'Year')['Yield'] \
        .sum() \
        .plot(kind='bar', figsize=(3,3))
    save_picture(plt, "各年份总产量.png", title="各年份总产量", xlabel="Year", ylabel="Yield")


def analysis_by_site_and_variety():
    """按地区和大麦种类"""
    fig, axes = plt.subplots(2,1)
    data.groupby(['Site','Variety'])['Yield'] \
        .sum() \
        .sort_values(ascending=False) \
        .head(10) \
        .sort_values() \
        .plot(kind='barh', figsize=(8,6), ax=axes[0])

    data.groupby(['Site','Variety'])['Yield'] \
        .sum() \
        .sort_values() \
        .head(10) \
        .sort_values() \
        .plot(kind='barh', figsize=(8,6), ax=axes[1])
    plt.xticks(range(0,121,20))
    save_picture(plt, "产量最高和产量最低的10个地区产量.png", xlabel="Yield")


def radar_for_site():
    """绘制各个地区关于每个大麦种类产量的雷达图"""
    # 分组求和
    data_series = data.groupby(['Site','Variety', 'Year'])['Yield'].sum()

    labels = np.array(varieties) # 标签
    labels_length = len(labels)  # 数据长度

    # 雷达图绘制
    def radar_chart(data1, data2, i):
        # 1931年
        data_radar1 = np.array(data1) # 数据
        angles1 = np.linspace(0, 2*np.pi, labels_length, endpoint=False)  # 分割圆周长
        data_radar1 = np.concatenate((data_radar1, [data_radar1[0]]))  # 闭合
        angles1 = np.concatenate((angles1, [angles1[0]]))  # 闭合
        # 1932年
        data_radar2 = np.array(data2) # 数据
        angles2 = np.linspace(0, 2*np.pi, labels_length, endpoint=False)  # 分割圆周长
        data_radar2 = np.concatenate((data_radar2, [data_radar2[0]]))  # 闭合
        angles2 = np.concatenate((angles2, [angles2[0]]))  # 闭合
        # 做极坐标系1931
        plt.polar(angles1, data_radar1, 'bo-', linewidth=1)
        # 做极坐标系1932
        plt.polar(angles2, data_radar2, 'go-', linewidth=1)
        # 填充1931
        plt.fill(angles1, data_radar1, facecolor='r', alpha=0.7)#
        # 填充1932
        plt.fill(angles2, data_radar2, facecolor='y', alpha=0.7)
        plt.thetagrids(angles1 * 180/np.pi, labels)  # 做标签
        plt.ylim(0, 71)
        plt.legend(['1931', '1932'])
        # 保存图片
        name = "radar/"+sites[i]+".png"
        save_picture(plt, name, title=sites[i])

    # 遍历绘制6个实验站
    for i in range(len(sites)):
        # 数据共120行,如前20行中1931年为偶数,1932年为奇数
        # 1931
        data1 = data_series[i*2*10 : (i*2+2)*10 : 2]
        # 1932
        data2 = data_series[i*2*10 + 1 : (i*2+2)*10 : 2]
        radar_chart(data1, data2, i)


def quarter_by_site():
    """计算各地区产量的四分位数,划分级别"""
    sort_data = data.groupby(['Site','Variety'])['Yield'].sum()

    for i in range(len(sites)):
        print('='*40)
        pd.set_option('precision', 2) # 保留2位小数
        # 获取数据
        site_data = sort_data[i*10:(i+1)*10].sort_values()
        # 四分位数
        upper_quarter = np.percentile(site_data, 75)
        lower_quarter = np.percentile(site_data, 25)
        # 打印
        print(site_data)
        print("\nquarter-75: {}, quarter-25: {}\n".format(upper_quarter, lower_quarter))
        # 极值标准化
        data_min = site_data.min()
        data_max = site_data.max()
        print("data standardization: ")
        print(((site_data-data_min) / (data_max-data_min)).sort_values())
        print('='*40)


def main():
    mkdir()
    mean_std()
    analysis_by_site()
    analysis_by_variety()
    analysis_by_year()
    analysis_by_site_and_variety()
    radar_for_site()
    quarter_by_site()


if __name__ == '__main__':
  main()

完整流程代码可见GitHub:

https://github.com/GYT0313/JupyterNotebook/tree/master/JupyterNotebook/matplotlib/data_visualization/shiyan/code

完!

2016-05-07 11:38:14 u010456562 阅读数 4021
  • Python数据分析实战-Pandas

    深度学习、机器学习和数据分析必须用pandas。pandas是在python直接流行的数据处理框架。可以说,如果不会使用pandas,就谈不上会用python做数据分析。本课程会使用奥林匹克一个真实的数据作为实验数据,从初级到各种pandas的常用操作,到常用的数据可视化,让你在短的时间内掌握好pandas,轻松愉快的玩转数据分析。

    2322 人正在学习 去看看 阿勒拉哈

说明:本文章为Python数据处理学习日志,主要内容来自书本《利用Python进行数据分析》,Wes McKinney著,机械工业出版社。

电影数据分析

所需文件在Day2中下载,接下来要用到的一些文件的文件格式如下:

users.dat文件格式
1::F::1::10::48067
2::M::56::16::70072
3::M::25::15::55117

ratings.dat文件格式
1::1193::5::978300760
1::661::3::978302109
1::914::3::978301968

movies.dat文件格式
1::Toy Story (1995)::Animation|Children's|Comedy
2::Jumanji (1995)::Adventure|Children's|Fantasy
3::Grumpier Old Men (1995)::Comedy|Romance

通过pandas.read_table将各个表分别读到pandas DataFrame对象中:

import pandas as pd
import os
path='E:\\Enthought\\book\\ch02\\movielens'
os.chdir(path) #改变当前工作目录到path

unames = ['user_id','gender','age','occupation','zip']
users = pd.read_table('users.dat',sep='::',header=None,names=unames) #根据'::'分解记录
-c:1: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators; you can avoid this warning by specifying engine='python'.

rnames = ['user_id','movie_id','rating','timestamp']
ratings = pd.read_table('ratings.dat',sep='::',header=None,names=rnames,engine='python') #加了engine='python'就不会出现上述报错

mnames = ['movie_id','title','genres']
movies = pd.read_table('movies.dat',sep='::',header=None,names=mnames,engine='python')

查看各个DataFrame对象:

users[:5]
Out[11]: 
   user_id gender  age  occupation    zip
0        1      F    1          10  48067
1        2      M   56          16  70072
2        3      M   25          15  55117
3        4      M   45           7  02460
4        5      M   25          20  55455

ratings[:5]
Out[12]: 
   user_id  movie_id  rating  timestamp
0        1      1193       5  978300760
1        1       661       3  978302109
2        1       914       3  978301968
3        1      3408       4  978300275
4        1      2355       5  978824291

movies[:5]
Out[13]: 
   movie_id                               title                        genres
0         1                    Toy Story (1995)   Animation|Children's|Comedy
1         2                      Jumanji (1995)  Adventure|Children's|Fantasy
2         3             Grumpier Old Men (1995)                Comedy|Romance
3         4            Waiting to Exhale (1995)                  Comedy|Drama
4         5  Father of the Bride Part II (1995)                        Comedy

其中年龄age,职业occupation是以编码形式给出,具体含义参见README。
接下来尝试分析散布在三个表中的数据。假设我们想根据性别和年龄计算某部电影的平均得分,如果将所有数据合并到一个表的话问题就简单多了。我们先用pandas的merge函数将ratings跟users 合并到一起,然后再将movies野合并进去。pandas会根据列明的重叠情况推断出哪些是合并(或连接)键:

data = pd.merge(pd.merge(ratings,users),movies)
data[:5] #可能输merge策略改变,接下来两个输出结果均与书本不同
Out[16]: 
   user_id  movie_id  rating  timestamp gender  age  occupation    zip  \
0        1      1193       5  978300760      F    1          10  48067   
1        2      1193       5  978298413      M   56          16  70072   
2       12      1193       4  978220179      M   25          12  32793   
3       15      1193       4  978199279      M   25           7  22903   
4       17      1193       5  978158471      M   50           1  95350   

                                    title genres  
0  One Flew Over the Cuckoo's Nest (1975)  Drama  
1  One Flew Over the Cuckoo's Nest (1975)  Drama  
2  One Flew Over the Cuckoo's Nest (1975)  Drama  
3  One Flew Over the Cuckoo's Nest (1975)  Drama  
4  One Flew Over the Cuckoo's Nest (1975)  Drama  

data.ix[0] #输出第一条记录
Out[17]: 
user_id                                            1
movie_id                                        1193
rating                                             5
timestamp                                  978300760
gender                                             F
age                                                1
occupation                                        10
zip                                            48067
title         One Flew Over the Cuckoo's Nest (1975)
genres                                         Drama
Name: 0, dtype: object

接下来就可以根据任意个用户或者电影属性对评分数据进行聚合操作。按性别计算每部电影的平均分,可以使用pivot_table方法:

mean_ratings = data.pivot_table('rating',index='title',columns='gender',aggfunc='mean') #参数改变rows-index,cols-columns,与书本不一样
mean_ratings[:5]
Out[26]: 
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()
ratings_by_title[:10]
Out[28]: 
title
$1,000,000 Duck (1971)                37
'Night Mother (1986)                  70
'Til There Was You (1997)             52
'burbs, The (1989)                   303
...And Justice for All (1979)        199
1-900 (1994)                           2
10 Things I Hate About You (1999)    700
101 Dalmatians (1961)                565
101 Dalmatians (1996)                364
12 Angry Men (1957)                  616
dtype: int64

active_titles = ratings_by_title.index[ratings_by_title>=250]
active_titles
Out[31]: 
Index([u''burbs, The (1989)', u'10 Things I Hate About You (1999)',
       u'101 Dalmatians (1961)', u'101 Dalmatians (1996)',
       u'12 Angry Men (1957)', u'13th Warrior, The (1999)',
       u'2 Days in the Valley (1996)', u'20,000 Leagues Under the Sea (1954)',
       u'2001: A Space Odyssey (1968)', u'2010 (1984)',
       ...
       u'X-Men (2000)', u'Year of Living Dangerously (1982)',
       u'Yellow Submarine (1968)', u'You've Got Mail (1998)',
       u'Young Frankenstein (1974)', u'Young Guns (1988)',
       u'Young Guns II (1990)', u'Young Sherlock Holmes (1985)',
       u'Zero Effect (1998)', u'eXistenZ (1999)'],
      dtype='object', name=u'title', length=1216)

该索引中含有评分数据大于250条的电影名称,然后就可以据此从前面的mean_ratings中选取所需的行了:

mean_ratings = mean_ratings.ix[active_titles]
mean_ratings[:5] #此处与书本不同
Out[34]: 
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_index(by='F',ascending=False)
-c:1: FutureWarning: by argument to sort_index is deprecated, pls use .sort_values(by=...)
#此处出现警告,pandas0.18.1版本sort_index没有by参数,具体见下
top_female_ratings = mean_ratings.sort_values(by='F',ascending=False)

top_female_ratings[:10]
Out[38]: 
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
Shawshank Redemption, The (1994)                    4.539075  4.560625
Grand Day Out, A (1992)                             4.537879  4.293255
To Kill a Mockingbird (1962)                        4.536667  4.372611
Creature Comforts (1990)                            4.513889  4.272277
Usual Suspects, The (1995)                          4.513317  4.518248

警告函数比较,pandas版本0.18.1

pandas.DataFrame.sort_index()
Parameters:
axis : index, columns to direct sorting
level : int or level name or list of ints or list of level names
if not None, sort on values in specified index level(s)
ascending : boolean, default True
Sort ascending vs. descending
inplace : bool, if True, perform operation in-place
kind : {quicksort, mergesort, heapsort}
Choice of sorting algorithm. See also ndarray.np.sort for more information. mergesort is the only stable algorithm. For DataFrames, this option is only applied when sorting on a single column or label.
na_position : {‘first’, ‘last’}
first puts NaNs at the beginning, last puts NaNs at the end
sort_remaining : bool
if true and sorting by level and index is multilevel, sort by other levels too (in order) after sorting by specified level
Returns:
sorted_obj : DataFrame

pandas.DataFrame.sort_values()
Parameters:
by : string name or list of names which refer to the axis items
axis : index, columns to direct sorting
ascending : bool or list of bool
Sort ascending vs. descending. Specify list for multiple sort orders. If this is a list of bools, must match the length of the by.
inplace : bool
if True, perform operation in-place
kind : {quicksort, mergesort, heapsort}
Choice of sorting algorithm. See also ndarray.np.sort for more information. mergesort is the only stable algorithm. For DataFrames, this option is only applied when sorting on a single column or label.
na_position : {‘first’, ‘last’}
first puts NaNs at the beginning, last puts NaNs at the end
Returns:
sorted_obj : DataFrame

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

mean_ratings['diff'] = mean_ratings['M']-mean_ratings['F']
sort_by_diff = mean_ratings.sort_values(by='diff')
sort_by_diff[:5]
Out[41]: 
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

堆排序结果反序并取前5行,得到的则是男性观众更喜爱的电影:

sort_by_diff[::-1][:5]
Out[43]: 
gender                                         F         M      diff
title                                                               
Good, The Bad and The Ugly, The (1966)  3.494949  4.221300  0.726351
Kentucky Fried Movie, The (1977)        2.878788  3.555147  0.676359
Dumb & Dumber (1994)                    2.697987  3.336595  0.638608
Longest Day, The (1962)                 3.411765  4.031447  0.619682
Cable Guy, The (1996)                   2.250000  2.863787  0.613787

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

#分组后计算标准差
rating_std_by_title = data.groupby('title')['rating'].std()
#筛选评分多于250条的
rating_std_by_title = rating_std_by_title.ix[active_titles]

rating_std_by_title.order(ascending=False)[:5]
-c:1: FutureWarning: order is deprecated, use sort_values(...) #虽有警告,依然能得出结果
rating_std_by_title.sort_values(ascending=False)[:5]
Out[50]: 
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
2016-05-08 17:27:50 u010456562 阅读数 10102
  • Python数据分析实战-Pandas

    深度学习、机器学习和数据分析必须用pandas。pandas是在python直接流行的数据处理框架。可以说,如果不会使用pandas,就谈不上会用python做数据分析。本课程会使用奥林匹克一个真实的数据作为实验数据,从初级到各种pandas的常用操作,到常用的数据可视化,让你在短的时间内掌握好pandas,轻松愉快的玩转数据分析。

    2322 人正在学习 去看看 阿勒拉哈

说明:本文章为Python数据处理学习日志,主要内容来自书本《利用Python进行数据分析》,Wes McKinney著,机械工业出版社。

1880-2010年间全美婴儿姓名

所需文件在Day2中下载,接下来要用到的一些文件的文件格式如下:

yob1880.txt-yob2010.txt
Mary,F,7065
Anna,F,2604
Emma,F,2003

整合数据

可以看到.txt文件中各个记录字段都以都好‘,’隔开,可以用pandas.read_csv将其加载到DataFrame中:

import pandas as pd
import os
path='E:\\Enthought\\book\\ch02\\names'
os.chdir(path)

names1880 = pd.read_csv('yob1880.txt',names=['name','sex','births'])
names1880[:5]
Out[8]: 
        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总计:

names1880.groupby('sex').births.sum()
Out[11]: 
sex
F     90993
M    110493
Name: births, dtype: int64

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

years = range(1880,2011)
pieces = []
columns = ['names', 'sex','births']

for year in years:
    path = 'yob%d.txt' % year
    frame = pd.read_csv(path, names=columns)
    frame['year'] = year
    pieces.append(frame)

names = pd.concat(pieces, ignore_index=True)
#将所有数据整合到单个DataFrame数据里面
names[:5]
Out[25]: 
       names sex  births  year
0       Mary   F    7065  1880
1       Anna   F    2604  1880
2       Emma   F    2003  1880
3  Elizabeth   F    1939  1880
4     Minnie   F    1746  1880

需要注意的有两点:

  1. concat默认是按行将多个DataFrame组合到一起。
  2. 必须指定ignore_index=True,因为我们不希望保留read_csv所返回的原始行号。

分析基本特征

现在我们得到一个非常大的DataFrame,它包含全部的名字数据。有了这些数据之后,我们就可以利用groupby或pivot_table在year和sex级别上对其进行聚合了:

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

total_births.tail() #查询最后5行数据
Out[36]: 
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')
Out[37]: <matplotlib.axes._subplots.AxesSubplot at 0x16485d68>

结果:

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

def add_prop(group):
    births = group.births
    #births = group.births.astype(float)
    #如果不是python3则要进行类型转换,因为整数除法回向下圆整
    group['prop'] = births/births.sum()
    return group

names = names.groupby(['year','sex']).apply(add_prop)
names[:5]
Out[42]: 
       names 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

在执行这样的分组处理时,一般都应该做一些有效性检查,比如验证所有分组的prop的总和是否为1。由于这是一个浮点数类型,所以我们用np.allclose来检查这个分总计值是否足够近似于(可能不会精确等于)1:

np.allclose(names.groupby(['year','sex']).prop.sum(),1)
Out[46]: True

为了便于实现进一步的分析,需要有去处该数据的一个子集:每对sex/year组合的前1000个名字。这又是一个分组操作:

 def get_top1000(group):
    return group.sort_values(by='births',ascending=False)[:1000]
#sort_index会出现warning,原因之前已说明

grouped = names.groupby(['year','sex'])
top1000 = grouped.apply(get_top1000)
top1000[:5]
Out[53]: 
                names sex  births  year      prop
year sex                                         
1880 F   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

现在的结果数据集就小多了,接下来的数据分析工作就针对这个top1000数据集了。

分析命名趋势

有了完整的数据集和刚才生产的top1000数据集,我们就可以开始分析各种命名趋势了。首先我们将前1000个名字分为男女两个部分:

boys = top1000[top1000.sex=='M']
girls = top1000[top1000.sex=='F']

这是两个简单的时间序列,只需要稍作整理即可绘制出相应的图表(比如每年叫做John和Mary的婴儿数)。我们先生成一张按year和name统计的总出生数透视表:

total_births = top1000.pivot_table('births',index='year',columns='names',aggfunc=sum)
#因为之前定义column时属性设置成了names,后面也跟着用这个了= =

total_births[:5]
Out[65]: 
names  Aaden  Aaliyah  Aarav  Aaron  Aarush  Ab  Abagail  Abb  Abbey  Abbie  \
year                                                                          
1880     NaN      NaN    NaN  102.0     NaN NaN      NaN  NaN    NaN   71.0   
1881     NaN      NaN    NaN   94.0     NaN NaN      NaN  NaN    NaN   81.0   
1882     NaN      NaN    NaN   85.0     NaN NaN      NaN  NaN    NaN   80.0   
1883     NaN      NaN    NaN  105.0     NaN NaN      NaN  NaN    NaN   79.0   
1884     NaN      NaN    NaN   97.0     NaN NaN      NaN  NaN    NaN   98.0   

names  ...    Zoa   Zoe  Zoey  Zoie  Zola  Zollie  Zona  Zora  Zula  Zuri  
year   ...                                                                 
1880   ...    8.0  23.0   NaN   NaN   7.0     NaN   8.0  28.0  27.0   NaN  
1881   ...    NaN  22.0   NaN   NaN  10.0     NaN   9.0  21.0  27.0   NaN  
1882   ...    8.0  25.0   NaN   NaN   9.0     NaN  17.0  32.0  21.0   NaN  
1883   ...    NaN  23.0   NaN   NaN  10.0     NaN  11.0  35.0  25.0   NaN  
1884   ...   13.0  31.0   NaN   NaN  14.0     6.0   8.0  58.0  27.0   NaN

subset = total_births[['John','Harry','Mary','Marilyn']]

subset.plot(subplots=True,figsize=(12,10),grid=False,title="Number of births per year")
Out[68]: 
array([<matplotlib.axes._subplots.AxesSubplot object at 0x0000000033237CC0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x0000000016085D30>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x000000002EAA6EF0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x0000000029259048>], dtype=object)

绘制结果:

  • 评估命名多样性增长
    上图所反映的境地情况可能意味着父母原意个小孩起常见的名字越来越少。这个假设可以从数据中得到验证。一个办法是计算最流行的1000个名字所占的比例,按year和sex进行聚合并绘图:
table = top1000.pivot_table('prop',index='year',columns='sex',aggfunc=sum)

table.plot(title="Sum of table1000.prop by year and sex",yticks=np.linspace(0,1.2,13),xticks=range(1880,2020,10))
Out[71]: <matplotlib.axes._subplots.AxesSubplot at 0x2e0dbeb8>

绘制结果:

上图结果表示,名字的多样性确实出现增长(前1000项的比例降低)。另一个办法是计算占总出生人口前50%的不同名字的数量,这个数字不太好计算。我们只考虑2010年男孩的名字:

df = boys[boys.year==2010]
df[:5]
Out[73]: 
                    names sex  births  year      prop
year sex                                             
2010 M   1676644    Jacob   M   21875  2010  0.011523
         1676645    Ethan   M   17866  2010  0.009411
         1676646  Michael   M   17133  2010  0.009025
         1676647   Jayden   M   17030  2010  0.008971
         1676648  William   M   16870  2010  0.008887

在按prop降序排列后,我们想知道前面多少个名字的人数加起来才够50%。虽然编写一个for循环也能达到目的,但NumPy有更聪明的矢量方法。先计算prop的累计和cumsum,然后通过searchsorted方法找到0.5应该被插在哪个位置才能保证不破坏顺序:

prop_cumsum = df.sort_values(by='prop',ascending=False).prop.cumsum()

prop_cumsum[:5]
Out[76]: 
year  sex         
2010  M    1676644    0.011523
           1676645    0.020934
           1676646    0.029959
           1676647    0.038930
           1676648    0.047817
Name: prop, dtype: float64

prop_cumsum.searchsorted(0.5)
Out[77]: array([116], dtype=int64) #注意这里的返回格式

由于数组索引从0开始,因此我们要给这个结果+1,即最终的结果为117。现在就对所有year/sex分组执行这个计算了。按这两个字段进行groupby处理,然后用一个函数计算个分组的这个值:

def get_quantile_count(group,q=0.5):
    group = group.sort_values(by='prop',ascending=False)
    return group.prop.cumsum().searchsorted(0.5)[0]+1
#注意!!!这里和书本不一样,上面看到python3的searchsorted()返回的是ndarray类型
#需要先取[0]元素,才能获得想要的数据,如果不作该处理,绘图会报错

diversity = top1000.groupby(['year','sex']).apply(get_quantile_count)
diversity = diversity.unstack('sex')
#依靠sex入栈操作,变Series为DataFrame

diversity.plot(title="Number of popular names in top 50%")
Out[129]: <matplotlib.axes._subplots.AxesSubplot at 0x218d7cf8>

上面碰到的问题,现在来仔细查看返回的各种类型:

prop_cumsum.searchsorted(0.5)
Out[132]: array([116], dtype=int64)

prop_cumsum.searchsorted(0.5)[0]
Out[133]: 116

type(prop_cumsum.searchsorted(0.5))
Out[134]: numpy.ndarray

type(prop_cumsum.searchsorted(0.5)[0])
Out[135]: numpy.int64

不作上述处理,则会出现下述错误:

diversity.plot(title="Number of popular names in top 50%",xticks=range(1880,2020,10))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-95-519f22a5ff7e> in <module>()
----> 1 diversity.plot(title="Number of popular names in top 50%",xticks=range(1880,2020,10))

E:\Enthought\hzk\User\lib\site-packages\pandas\tools\plotting.pyc in __call__(self, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
   3561                            colormap=colormap, table=table, yerr=yerr,
   3562                            xerr=xerr, label=label, secondary_y=secondary_y,
-> 3563                            **kwds)
   3564     __call__.__doc__ = plot_series.__doc__
   3565 

E:\Enthought\hzk\User\lib\site-packages\pandas\tools\plotting.pyc in plot_series(data, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
   2640                  yerr=yerr, xerr=xerr,
   2641                  label=label, secondary_y=secondary_y,
-> 2642                  **kwds)
   2643 
   2644 

E:\Enthought\hzk\User\lib\site-packages\pandas\tools\plotting.pyc in _plot(data, x, y, subplots, ax, kind, **kwds)
   2436         plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
   2437 
-> 2438     plot_obj.generate()
   2439     plot_obj.draw()
   2440     return plot_obj.result

E:\Enthought\hzk\User\lib\site-packages\pandas\tools\plotting.pyc in generate(self)
   1021     def generate(self):
   1022         self._args_adjust()
-> 1023         self._compute_plot_data()
   1024         self._setup_subplots()
   1025         self._make_plot()

E:\Enthought\hzk\User\lib\site-packages\pandas\tools\plotting.pyc in _compute_plot_data(self)
   1130         if is_empty:
   1131             raise TypeError('Empty {0!r}: no numeric data to '
-> 1132                             'plot'.format(numeric_data.__class__.__name__))
   1133 
   1134         self.data = numeric_data

TypeError: Empty 'DataFrame': no numeric data to plot

原因如下:

diversity.dtypes #这是没有取[0]的结果
Out[109]: 
sex
F    object #"no numeric data to plot"因为不是数字类型
M    object #"no numeric data to plot"因为不是数字类型
dtype: object 

diversity.dtypes #取[0]后均变为int64
Out[136]: 
sex
F    int64
M    int64
dtype: object

图像绘制结果:

从上图中可以看出,女孩的名字的多样性总是比男孩的高,而且还在越来越高。

  • “最后一个字母”的变革
    2007年,一名婴儿姓名研究人员Laura Wattenberg在她自己的网站上指出(http://www.babynamewicard.com):近百年来,男孩名字在最后一个字母的分布发生了显著的变化。为了了解具体情况,首先将全部出生数据在年度、性别以及末位字母上进行聚合:
get_last_letter = lambda x:x[-1]
last_letters = names.names.map(get_last_letter)
last_letters.names = 'last_letter'

table = names.pivot_table('births',index=last_letters,columns=['sex','year'],aggfunc=sum)

subtable = table.reindex(columns=[1910,1960,2010],level='year')

subtable.head()
Out[143]: 
sex           F                            M                    
year       1910      1960      2010     1910      1960      2010
names                                                           
a      108376.0  691247.0  670605.0    977.0    5204.0   28438.0
b           NaN     694.0     450.0    411.0    3912.0   38859.0
c           5.0      49.0     946.0    482.0   15476.0   23125.0
d        6750.0    3729.0    2607.0  22111.0  262112.0   44398.0
e      133569.0  435013.0  313833.0  28655.0  178823.0  129012.0

接下来,我们需要按总出生数对该表进行规范化处理,以便计算出各性别各末位字母占总出生人数的比例:

subtable.sum()
Out[144]: 
sex  year
F    1910     396416.0
     1960    2022062.0
     2010    1759010.0
M    1910     194198.0
     1960    2132588.0
     2010    1898382.0
dtype: float64

letter_prop = subtable/subtable.sum() #转换类型.astype(float)

有了这个字母比例数据后,就可以生成一张各年度各性别的条形图了:

import matplotlib.pyplot as plt

fig,axes = plt.subplots(2,1,figsize=(10,8))

letter_prop['M'].plot(kind='bar',rot=0,ax=axes[0],title='Male')
Out[149]: <matplotlib.axes._subplots.AxesSubplot at 0x2b7ced30>

letter_prop['F'].plot(kind='bar',rot=0,ax=axes[1],title='Female',legend=False)
Out[150]: <matplotlib.axes._subplots.AxesSubplot at 0x213fd860>

图像:

从上图可以看出,从20世纪60年代开始,以字母“n”结尾的男孩子名字出现显著的增长。回到之前创建的那个完整表,按年度和性别对其进行规范化处理,并在男孩子名字中选出几个字母,最后进行转置以便将各个列做成一个时间序列:

letter_prop = table / table.sum()

dny_ts = letter_prop.ix[['d','n','y'],'M'].T

dny_ts.head()
Out[154]: 
names         d         n         y
year                               
1880   0.083055  0.153213  0.075760
1881   0.083247  0.153214  0.077451
1882   0.085340  0.149560  0.077537
1883   0.084066  0.151646  0.079144
1884   0.086120  0.149915  0.080405

有了这个时间序列的DataFrame之后,就可以通过其plot方法绘制出一张趋势图了:

dny_ts.plot()
Out[155]: <matplotlib.axes._subplots.AxesSubplot at 0x2b7ce9b0>

趋势图:

  • 变成女孩名字的男孩名字(以及相反的情况)
    另一个有趣的趋势是,早年流行于男孩的名字近年来“变形了”,例如Lesley或Leslie。回到top1000数据集,找出其中以“lesl”开头的一组名字:
all_names = top1000.names.unique()

mask  =np.array(['lesl' in x.lower() for x in all_names])

lesley_like = all_names[mask]

lesley_like
Out[159]: array(['Leslie', 'Lesley', 'Leslee', 'Lesli', 'Lesly'], dtype=object)

然后利用这个结果过滤其他的名字,并按名字分组计算出生数已查看相对频率:

filtered = top1000[top1000.names.isin(lesley_like)]

filtered.groupby('names').births.sum()
Out[162]: 
names
Leslee      1082
Lesley     35022
Lesli        929
Leslie    370429
Lesly      10067
Name: births, dtype: int64

接下来,我们按性别和年度进行聚合,并按年度进行规范化处理:

table = filtered.pivot_table('births',index='year',columns='sex',aggfunc=sum)

table = table.div(table.sum(1),axis=0)

table.tail()
Out[172]: 
sex     F   M
year         
2006  1.0 NaN
2007  1.0 NaN
2008  1.0 NaN
2009  1.0 NaN
2010  1.0 NaN

table.plot(style={'M':'k-','F':'k--'})
Out[173]: <matplotlib.axes._subplots.AxesSubplot at 0x2cd089e8>

各年度使用“Lesley型”名字的男女比例:

2019-10-22 22:15:14 weixin_44163765 阅读数 45
  • Python数据分析实战-Pandas

    深度学习、机器学习和数据分析必须用pandas。pandas是在python直接流行的数据处理框架。可以说,如果不会使用pandas,就谈不上会用python做数据分析。本课程会使用奥林匹克一个真实的数据作为实验数据,从初级到各种pandas的常用操作,到常用的数据可视化,让你在短的时间内掌握好pandas,轻松愉快的玩转数据分析。

    2322 人正在学习 去看看 阿勒拉哈

python数据分析依赖于第三方库,其中numpy、pandas、matplotlib是经常使用的,本人大致学习了这几个库,基本的操作了解一些,学习的目的是为了能在实际中应用,下面将对《利用Python进行数据分析》书中的示例进行操作,期望学到的东西能融合贯通。(原始数据可在GitHub上下载)

进行示例之前需要了解的知识

JSON与dict 的比较
JSON(JavaScript Object Notation)
是一种数据格式,纯字符串
dict
是一种数据结构,是对Hash Table这一数据结构的实现

在python中,json和dict非常类似,都是以key-value的形式存在,而且json、dict也可以非常方便的通过dumps、loads互转
区别:
1.json中key只能是字符串(数字也不例外),且强制是双引号,value如果是数字可以不加双引号
dict相对自由,可以是双引号,也可以是单引号
2.json的类型是字符串(边界符是单引号)
dict的类型是字典
当然还有其他区别,暂时先了解这么多吧!

进入正题
由于在读取文件之前,只知道是txt文本文件,具体内部是什么样的我们不知道,先打开文件看看。
数据格式是JSON
这是一个JSON格式的数据

1. 利用纯python代码进行时区计数

列表推导
列表中的每一项
我们的目的就是想知道数据集中最常出现的是哪个时区(tz 字段),即对时区进行统计
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2016-05-06 16:56:27 u010456562 阅读数 10317
  • Python数据分析实战-Pandas

    深度学习、机器学习和数据分析必须用pandas。pandas是在python直接流行的数据处理框架。可以说,如果不会使用pandas,就谈不上会用python做数据分析。本课程会使用奥林匹克一个真实的数据作为实验数据,从初级到各种pandas的常用操作,到常用的数据可视化,让你在短的时间内掌握好pandas,轻松愉快的玩转数据分析。

    2322 人正在学习 去看看 阿勒拉哈

说明:本文章为Python数据处理学习日志,主要内容来自书本《利用Python进行数据分析》,Wes McKinney著,机械工业出版社。

“以我的观点来看,如果只需要用Python进行高效的数据分析工作,根本就没必要非得成为通用软件编程方面的专家不可。”——作者

接下来是书本一些代码的实现,用来初步了解Python处理数据的功能,相关资源可在下方链接下载。
书本相关资源

读取文件第一行

相关例子可以再shell中执行,也可以再Canopy Editor中执行。以下代码在Canopy Editor中执行。

path='E:\\Enthought\\book\\ch02\\usagov_bitly_data2012-03-16-1331923249.txt'
open(path).readline()

在shell中执行则不要转置符’\’,path为存放文件路径。在shell中执行一定要用pylab模式打开,不然绘图会出现些小问题。

path='E:\Enthought\book\ch02\usagov_bitly_data2012-03-16-1331923249.txt'

输出结果:

Out[2]: '{ "a": "Mozilla\\/5.0 (Windows NT 6.1; WOW64) AppleWebKit\\/535.11 (KHTML, like Gecko) Chrome\\/17.0.963.78 Safari\\/535.11", "c": "US", "nk": 1, "tz": "America\\/New_York", "gr": "MA", "g": "A6qOVH", "h": "wfLQtf", "l": "orofrog", "al": "en-US,en;q=0.8", "hh": "1.usa.gov", "r": "http:\\/\\/www.facebook.com\\/l\\/7AQEFzjSi\\/1.usa.gov\\/wfLQtf", "u": "http:\\/\\/www.ncbi.nlm.nih.gov\\/pubmed\\/22415991", "t": 1331923247, "hc": 1331822918, "cy": "Danvers", "ll": [ 42.576698, -70.954903 ] }\n'

以上结果为纯文本加载。
Python中的内置或者第三方模块将JSON字符串转化为Python字典对象。这里,将使用json模块及其loads函数逐行加载已经下载好的数据文件:

import json
path='E:\\Enthought\\book\\ch02\\usagov_bitly_data2012-03-16-1331923249.txt'
records=[json.loads(line) for line in open(path)]
records[0]

这样,records对象就成了一组Python字典了。

Out[6]: 
{u'a': u'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 Safari/535.11',
 u'al': u'en-US,en;q=0.8',
 u'c': u'US',
 u'cy': u'Danvers',
 u'g': u'A6qOVH',
 u'gr': u'MA',
 u'h': u'wfLQtf',
 u'hc': 1331822918,
 u'hh': u'1.usa.gov',
 u'l': u'orofrog',
 u'll': [42.576698, -70.954903],
 u'nk': 1,
 u'r': u'http://www.facebook.com/l/7AQEFzjSi/1.usa.gov/wfLQtf',
 u't': 1331923247,
 u'tz': u'America/New_York',
 u'u': u'http://www.ncbi.nlm.nih.gov/pubmed/22415991'}

现在,只要一字符串形式给出想要访问的键就可以得到当前记录中相应的值了:

records[0]['tz']
Out[7]: u'America/New_York'

print records[0]['tz']
America/New_York

对时区进行计数

1.用纯Python对时区进行计数

假设我们想知道该数据集中最常出现的是哪个时区(即tz字段)。首先,我们用列表推导式取出一组时区:

time_zones=[rec['tz'] for rec in records if 'tz' in rec]
time_zones[:10] #获取前10条记录的tz

结果:

Out[12]: 
[u'America/New_York',
 u'America/Denver',
 u'America/New_York',
 u'America/Sao_Paulo',
 u'America/New_York',
 u'America/New_York',
 u'Europe/Warsaw',
 u'',
 u'',
 u'']

接下来对时区进行计数:

 def get_counts(sequence):
    counts={}
    for x in sequence:
        if x in counts:
            counts[x]+=1
        else:
            counts[x]=1
    return counts

或者使用Python标准库

def get_counts2(sequence):
    counts=defaultdict(int) #所有初始值均会初始化为0
    for x in sequence:
        counts[x]+=1
    return counts

调用函数

counts = get_counts(time_zones)

counts['America/New_York']
Out[20]: 1251

len(time_zones)
Out[21]: 3440

如果只想要得到前10位的时区及其计数值,我们需要用到一些关羽字典的处理技巧:

def top_counts(count_dict,n=10):
    value_key_pairs=[(count,tz) for tz,count in count_dict.items()]
    value_key_pairs.sort()
    return value_key_pairs[-n:]

数据处理结果

top_counts(counts)
Out[26]: 
[(33, u'America/Sao_Paulo'),
 (35, u'Europe/Madrid'),
 (36, u'Pacific/Honolulu'),
 (37, u'Asia/Tokyo'),
 (74, u'Europe/London'),
 (191, u'America/Denver'),
 (382, u'America/Los_Angeles'),
 (400, u'America/Chicago'),
 (521, u''),
 (1251, u'America/New_York')]

或者在Python标准库中找到collections.Counter类,处理起来就比较简单:

from collections import Counter
counts=Counter(time_zones)
counts.most_common(10)
Out[29]: 
[(u'America/New_York', 1251),
 (u'', 521),
 (u'America/Chicago', 400),
 (u'America/Los_Angeles', 382),
 (u'America/Denver', 191),
 (u'Europe/London', 74),
 (u'Asia/Tokyo', 37),
 (u'Pacific/Honolulu', 36),
 (u'Europe/Madrid', 35),
 (u'America/Sao_Paulo', 33)]

2.用pandas对时区进行计数

DataFrame是pandas中重要的数据结构,它用于将数据表示成一个表格。

from pandas import DataFrame,Series
import pandas as pd;import numpy as np
frame = DataFrame(records)

结果

frame['tz'][:10]
Out[34]: 
0     America/New_York
1       America/Denver
2     America/New_York
3    America/Sao_Paulo
4     America/New_York
5     America/New_York
6        Europe/Warsaw
7                     
8                     
9                     
Name: tz, dtype: object

frame[‘tz’]所返回的Series对象有一个value_counts方法,该方法可以让我们得到所需要的信息:

tz_counts = frame['tz'].value_counts()
tz_counts[:10]
Out[36]: 
America/New_York       1251
                        521
America/Chicago         400
America/Los_Angeles     382
America/Denver          191
Europe/London            74
Asia/Tokyo               37
Pacific/Honolulu         36
Europe/Madrid            35
America/Sao_Paulo        33
Name: tz, dtype: int64

绘图

然后,我们可以利用绘图库(即matplotlib)为该段数据生成一张图片。为此,我们先给记录中位未知或者缺失的时区填上一个替代值。fillna函数可以替换缺失值(NA),而未知值(空字符串)则可以通过布尔型数组索引加以替换:

clean_tz=frame['tz'].fillna('Missing')
clean_tz[clean_tz==''] = 'Unknown'
tz_counts=clean_tz.value_counts()
tz_counts[:10]
Out[41]: 
America/New_York       1251
Unknown                 521
America/Chicago         400
America/Los_Angeles     382
America/Denver          191
Missing                 120
Europe/London            74
Asia/Tokyo               37
Pacific/Honolulu         36
Europe/Madrid            35
Name: tz, dtype: int64

绘图

tz_counts[:10].plot(kind='barh',rot=0)
Out[42]: <matplotlib.axes._subplots.AxesSubplot at 0xe50b1d0>

结果
示例数据中最常出现的时区

对Agent(‘a’)进行划分

‘a’字段含有执行URL缩短操作的浏览器、设备、应用程序的相关信息:

frame['a'][0]
Out[43]: u'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 Safari/535.11'

运用Python内置的字符串函数和正则表达式,可以将这种字符串的第一节(与浏览器大致对应)分离出来并得到另外一份用户行为摘要:

results = Series([x.split()[0] for x in frame.a.dropna()]) #按空格切分字段'a'
results[:5]
Out[45]: 
0               Mozilla/5.0
1    GoogleMaps/RochesterNY
2               Mozilla/4.0
3               Mozilla/5.0
4               Mozilla/5.0
dtype: object
results.value_counts()[:8]
Out[46]: 
Mozilla/5.0                 2594
Mozilla/4.0                  601
GoogleMaps/RochesterNY       121
Opera/9.80                    34
TEST_INTERNET_AGENT           24
GoogleProducer                21
Mozilla/6.0                    5
BlackBerry8520/5.0.0.681       4
dtype: int64

现在,根据Windows用户和非Windows用户对时区统计信息进行分解。为了方便起见,嘉定只要agent字符串中含有“Windows”就认为该用户为Windows用户。

cframe = frame[frame.a.notnull()] #删除agent为空的记录
 operating_system = np.where(cframe['a'].str.contains('Windows'),'Windows','Not Windows')
operating_system[:5]
Out[52]: #可能是版本原因,此处略有不同,原书以frame形式输出
array(['Windows', 'Not Windows', 'Windows', 'Not Windows', 'Windows'], 
      dtype='|S11')

接下来就可以根据时区和新得到的操作系统列表对数据进行分组,然后通过size对分组结果进行计数(类似于上面的value_counts函数),并利用unstack对技术结果进行重塑:

by_tz_os = cframe.groupby(['tz',operating_system])
agg_counts = by_tz_os.size().unstack().fillna(0)
agg_counts[:10]
Out[56]: 
                                Not Windows  Windows
tz                                                  
                                      245.0    276.0
Africa/Cairo                            0.0      3.0
Africa/Casablanca                       0.0      1.0
Africa/Ceuta                            0.0      2.0
Africa/Johannesburg                     0.0      1.0
Africa/Lusaka                           0.0      1.0
America/Anchorage                       4.0      1.0
America/Argentina/Buenos_Aires          1.0      0.0
America/Argentina/Cordoba               0.0      1.0
America/Argentina/Mendoza               0.0      1.0

最后,选出最常出现的时区。根据agg_counts中的行数构造一个间接索引数组:

indexer = agg_counts.sum(1).argsort()
indexer[:10]
Out[58]: 
tz
                                  24
Africa/Cairo                      20
Africa/Casablanca                 21
Africa/Ceuta                      92
Africa/Johannesburg               87
Africa/Lusaka                     53
America/Anchorage                 54
America/Argentina/Buenos_Aires    57
America/Argentina/Cordoba         26
America/Argentina/Mendoza         55
dtype: int64

然后通过take按照这个顺序截取最后10行:

count_subset = agg_counts.take(indexer)[-10:]
count_subset
Out[60]: 
                     Not Windows  Windows
tz                                       
America/Sao_Paulo           13.0     20.0
Europe/Madrid               16.0     19.0
Pacific/Honolulu             0.0     36.0
Asia/Tokyo                   2.0     35.0
Europe/London               43.0     31.0
America/Denver             132.0     59.0
America/Los_Angeles        130.0    252.0
America/Chicago            115.0    285.0
                           245.0    276.0
America/New_York           339.0    912.0

生成图形

count_subset.plot(kind='barh',stacked=True)
Out[61]: <matplotlib.axes._subplots.AxesSubplot at 0xeb89d68>

结果

为了看清楚小分组中Windows用户的相对比例,可以将各行规范化为“总计为1”并重新绘图:

normed_subset = count_subset.div(count_subset.sum(1),axis=0)
normed_subset.plot(kind='barh',stacked=True)
Out[63]: <matplotlib.axes._subplots.AxesSubplot at 0xed1e1d0>

结果

没有更多推荐了,返回首页