目录
#import相关的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
1. 数据载入和总览
1.1 数据载入(pd.read_csv())
由于单个数据集过大,我把数据集按照0-50w行,50-100w行,100w+切分成3份(data_01.csv/data_02.csv/data_03.csv),并截取了所需的字段
pandas.read_csv(filepath, sep=', ' ,header='infer', names=None)
filepath:文本文件路径;sep:分隔符;header默认使用第一行作为列名,如果header=None则pandas为其分配默认的列名;也可使用names传入列表指定列名
#数据读入使用pd.read_csv()
data_01 = pd.read_csv('./data_01.csv')
data_02 = pd.read_csv('./data_02.csv')
data_03 = pd.read_csv('./data_03.csv')
有3种表示路径的方法--> r".\data.csv" ; ".\\data.csv" ; "./data.csv"。
1.2 数据合并(pd.concat())
pandas.concat可以沿着一条轴将多个对象堆叠到一起,这里我们要把data_01.csv/data_02.csv/data_03.csv三份数据集合并为一份数据进行后续分析处理
pd.concat(objs, axis=0, join='outer')
合并多个对象时需要放到一个列表中;axis=0默认按照0轴堆叠,axis=1指合并列;concat函数可按照其他轴的逻辑关系进行合并,默认join='outer',还有一个可取的值是'inner'
data = pd.concat([data_01,data_02,data_03])
各字段含义
- cand_nm – 接受捐赠的总统候选人姓名
- contbr_nm – 捐赠人姓名
- contbr_st – 捐赠人所在州
- contbr_employer – 捐赠人所在公司
- contbr_occupation – 捐赠人职业
- contb_receipt_amt – 捐赠数额(美元)
- contb_receipt_dt – 收到捐款的日期
1.3 数据预览和基本统计分析
#查看前5行数据
data.head()
#查看数据的信息,包括每个字段的名称、非空数量、字段的数据类型
data.info()
#用统计学指标描述数字数据的特征概要,标准差等
data.describe()
2. 数据清洗
2.1 缺失值处理
按照自己的意愿或实际情况,用dropna()获得删除缺失值之后的数据或用fillna()填充缺失值。
#从data.info()得知,contbr_employer、contbr_occupation均有少量缺失值,均填充为NOT PROVIDED
data['contbr_employer'].fillna('NOT PROVIDED',inplace=True)
data['contbr_occupation'].fillna('NOT PROVIDED',inplace=True)
data.info()
2.2 数据转换
利用字典映射进行转换:党派分析
美国大选一般是民主党和共和党之争,虽然数据中没有党派这个字段,但是通过候选人名称即cand_nm,可以得到对应的党派信息
# 用unique()方法得到一列中不同名称的数组。以此查看数据中总统候选人都有谁
print('共有{}位候选人,分别是'.format(len(data['cand_nm'].unique())))
data['cand_nm'].unique() #可直接print出来
#通过搜索引擎等途径,获取到每个总统候选人的所属党派,建立字典parties,候选人名字作为键,所属党派作为对应的值
parties = {'Bachmann, Michelle': 'Republican',
'Cain, Herman': 'Republican',
'Gingrich, Newt': 'Republican',
'Huntsman, Jon': 'Republican',
'Johnson, Gary Earl': 'Republican',
'McCotter, Thaddeus G': 'Republican',
'Obama, Barack': 'Democrat',
'Paul, Ron': 'Republican',
'Pawlenty, Timothy': 'Republican',
'Perry, Rick': 'Republican',
"Roemer, Charles E. 'Buddy' III": 'Republican',
'Romney, Mitt': 'Republican',
'Santorum, Rick': 'Republican'}
增加一列party存储党派信息
#通过map映射函数,增加一列party存储党派信息
data['party']=data['cand_nm'].map(parties)
data.shape # 查看数据的基本情况-->几行几列
data.tail() # 查看数据帧后5行
#查看两个党派的情况
data['party'].value_counts()
可以看出Republican(共和党)总统候选人获得的赞助少,Democrat(民主党)获得的赞助次数更多一些
排序:按照职业汇总对赞助总金额进行排序
按照职位进行汇总,计算赞助总金额,展示前20项,发现不少职业是相同的,只不过是表达不一样而已,如C.E.O.与CEO,都是一个职业
DataFrame.sort_values(by, ascending=True, inplace=False)
by是根据哪一列进行排序,可以传入多列;ascending=True是升序排序,False为降序;inplace=Ture则是修改原dataframe,默认为False
# 先用groupby()方法对职位进行汇总,同时对应其赞助金额,以sum()计算各职位赞助总金额
data.groupby('contbr_occupation')['contb_receipt_amt'].sum()
# 赞助总金额前20的职位和赞助金额
data.groupby('contbr_occupation')['contb_receipt_amt'].sum().sort_values(ascending=False)[:20]
利用函数进行数据转换:职业与雇主信息分析
许多职业都涉及相同的基本工作类型,下面我们来清理一下这样的数据(这里巧妙地利用了dict.get它允许没有映射关系的职业也能“通过”)
#建立一个职业对应字典,把相同职业的不同表达映射为对应的职业,比如把C.E.O.映射为CEO
occupation_map = {
'INFORMATION REQUESTED PER BEST EFFORTS':'NOT PROVIDED',
'INFORMATION REQUESTED':'NOT PROVIDED',
'SELF' : 'SELF-EMPLOYED',
'SELF EMPLOYED' : 'SELF-EMPLOYED',
'C.E.O.':'CEO',
'LAWYER':'ATTORNEY',
}
# 如果不在字典中,返回x
f = lambda x: occupation_map.get(x, x)
data.contbr_occupation = data.contbr_occupation.map(f)
同样地,对雇主信息进行类似转换
employer_map = {
'INFORMATION REQUESTED PER BEST EFFORTS' : 'NOT PROVIDED',
'INFORMATION REQUESTED' : 'NOT PROVIDED',
'SELF' : 'SELF-EMPLOYED',
'SELF EMPLOYED' : 'SELF-EMPLOYED',
}
# If no mapping provided, return x
f = lambda x: employer_map.get(x, x)
data.contbr_employer = data.contbr_employer.map(f)
此时再用data.contbr_employer或data["contbr_employer"]对雇主人员或职工人员进行查看,身份已经统一名称了。
2.3 数据筛选
赞助金额筛选
赞助包括退款(负的出资额),为了简化分析过程,我们限定数据集只有正出资额
data = data[data['contb_receipt_amt']>0]
# data=data[data.contb_receipt_amt>0]
候选人筛选(Obama、Romney)
从下面可以看出,赞助基本集中在Obama、Romney之间,为了更好的聚焦两者间的竞争,我们选取这两位候选人的数据子集作进一步分析
#查看各候选人获得的赞助总金额排名。默认ascending=True从上到下依次增加,为了观察方便,建议ascending=False
data.groupby('cand_nm')['contb_receipt_amt'].sum().sort_values(ascending=False)
#抽取候选人为Obama、Romney的子集数据
data_sub = data[data['cand_nm'].isin(['Obama, Barack','Romney, Mitt'])].copy()
data_sub
2.4 面元化数据
接下来我们对该数据做另一种非常实用的分析,利用cut函数根据出资额大小将数据离散化到多个面元中:
bins = np.array([0,1,10,100,1000,10000,100000,1000000,10000000])
labels = pd.cut(data_sub['contb_receipt_amt'],bins)
labels
3. 数据聚合与分组运算
分组计算Grouping,分组运算是一个“split-apply-combine”的过程:
- 拆分,pandas对象中的数据会根据你所提供的一个或多个键被拆分为多组
- 应用,将一个函数应用到各个分组并产生一个新值
- 合并,所有这些函数的执行结果会合并到最终的结果对象中
# 此图表明groupby的原理
from IPython.display import Image
Image('groupby.png')
3.1 透视表(pivot_table)分析党派和职业
我们可以通过pivot_table根据党派和职业对数据进行聚合,然后过滤掉总出资不足200万美元的数据:
#按照党派、职业对赞助金额进行汇总,类似excel中的透视表操作,聚合函数为sum
by_occupation = data.pivot_table('contb_receipt_amt',index='contbr_occupation',columns='party',aggfunc='sum') # values='contb_receipt_amt'
by_occupation
#过滤掉赞助金额小于200W的数据
over_2million=by_occupation[by_occupation.sum(1)>2000000]
over_2million
sum(1)解释:by_occupation 是二维的数据,因此0指代party,1指代contbr_occupation,那么by_occupation.sum(1)就是按照行(contbr_occupation维度)聚合。
over_2million.plot(kind='bar')
3.2 分组级运算和转换
根据职业与雇主信息分组运算
我们接下来了解一下对Obama和Romney总出资最高的职业和雇主。注意,这里巧妙地利用了dict.get,它允许没有映射关系的职业也能“通过”
#由于职业和雇主的处理非常相似,我们定义函数get_top_amounts()对两个字段进行分析处理
def get_top_amounts(group,key,n=5): # n的赋值可随便写,只是一中间参数
#传入groupby分组后的对象,返回按照key字段汇总的排序前n的数据
totals = group.groupby(key)['contb_receipt_amt'].sum()
return totals.sort_values(ascending=False)[:n]
grouped = data_sub.groupby('cand_nm')
grouped.apply(get_top_amounts,'contbr_occupation',n=7)
从数据可以看出,Obama更受精英群体(律师、医生、咨询顾问)的欢迎,Romney则得到更多企业家或企业高管的支持。
#同样的,调用get_top_amounts()对雇主进行分析处理
grouped.apply(get_top_amounts,'contbr_employer',n=10)
可看出Obama的支持雇主:微软、盛德国际律师事务所; Romney:瑞士瑞信银行、摩根斯坦利、高盛公司、巴克莱资本、H.I.G.资本。
对赞助金额进行分组分析(matplotlib画图)
前面我们已经利用pd.cut()函数,根据出资额大小将数据离散化到多个面元中,接下来我们就要对每个离散化的面元进行分组分析
首先统计各出资区间的赞助笔数,这里用到unstack(),stack()函数是堆叠,unstack()函数就是不要堆叠,即把多层索引变为表格数据
#labels是之前赞助金额离散化后的Series
grouped_bins = data_sub.groupby(['cand_nm',labels])
grouped_bins.size().unstack(0)
接下来,我们再统计各区间的赞助金额
bucket_sums=grouped_bins['contb_receipt_amt'].sum().unstack(0)
bucket_sums
#Obama、Romney各区间赞助总金额
bucket_sums.plot(kind='bar')
上图虽然能够大概看出Obama、Romney的赞助金额区间分布,但对比并不够突出,如果用百分比堆积图效果会更好,下面我们就实现以下。
#算出每个区间两位候选人收到赞助总金额的占比
normed_sums = bucket_sums.div(bucket_sums.sum(axis=1),axis=0)
normed_sums
#使用柱状图,指定stacked=True进行堆叠,即可完成百分比堆积图
normed_sums[:-2].plot(kind='bar',stacked=True)
可以看出,小额赞助方面,Obama获得的数量和金额比Romney多得多。
按照赞助人姓名分组计数,计算重复赞助次数最多的前20人
data.groupby('contbr_nm')['contbr_nm'].count().sort_values(ascending=False)[:20]
4.时间处理
4.1 str转datetime
我们可以使用to_datetime方法解析多种不同的日期表示形式。对标准日期格式(如ISO8601)的解析非常快。我们也可以指定特定的日期解析格式,如pd.to_datetime(series,format='%Y%m%d')
data_sub['time']=pd.to_datetime(data_sub['contb_receipt_dt'])
data_sub['time']
注意从dtype的str类型转为datatime64的正规时间类型
4.2 以时间作为索引
data_vs.set_index('time',inplace=True)
data_vs.head()
4.3重采样和频度转换
重采样(Resampling)指的是把时间序列的频度变为另一个频度的过程。把高频度的数据变为低频度叫做降采样(downsampling),resample会对数据进行分组,然后再调用聚合函数。这里我们把频度从每日转换为每月,属于高频转低频的降采样。
data_vs.groupby('cand_nm').resample('M')['cand_nm'].count()
resample('M')把时间以按"月”重构,并用count()统计出每个月的赞助人数情况。
vs_time = data_vs.groupby('cand_nm').resample('M')['cand_nm'].count()
vs_time.unstack(0)
unstack()函数把多层索引变为表格数据,注意观察unstack(0)和unstack(1)的效果。
sub_time.unstack(1)
我们用面积图把11年4月-12年4月两位总统候选人接受的赞助个数做对比可以看出,越临近竞选,大家赞助的热情越高涨,奥巴马在各个时段都占据绝对的优势
fig1, ax1=plt.subplots(figsize=(32,8))
vs_time.unstack(0).plot(kind='area',ax=ax1,alpha=0.6)
plt.show() # plt.show()可写可不写,因为plot本身即可成像。
注意在自定义图像的大小时,如上用法:fig1, ax1=plt.subplots(figsize=(32,8)),以子图方法subplots定义图像大小,fig1、ax1二个接受变量必不可少,figsize尺寸自行设置。