《利用Python进行数据分析》读书笔记。
第 2 章:引言
介绍数据分析的一般步骤,并用三个实例说明如何用 python 进行数据分析。
所有用到的数据可以从作者的 github下载。
数据分析的一般步骤
数据加载
从各种格式的数据文件和数据库加载数据
数据准备
对数据进行清理、修整、整合、规范化、重塑、切片切块、变形等处理,以便于进行分析
数据转换
对数据集进行数学和统计运算,产生新的数据集。比如,根据分组变量对一个大表进行聚合
建模和计算
通过统计模型、机器学习算法和其他计算工具,对数据进行分析计算
结果展示
通过静态或交互式的方式,展示结果
例子:分析网站的用户访问数据
%pylab inline
import pandas as pd
import json
将数据加载到 DataFrame¶
records = [json.loads(line) for line in open('../data/datasets/bitly_usagov/example.txt')]
frame = pd.DataFrame(records) # 从列表创建 DataFrame
数据准备¶
# 规整时区数据
frame['tz'].fillna('Missing') # 填充 缺失值 NA
frame[frame['tz']=='']='Unknown' # 填充空值
数据转换¶
# 按值统计个数
tz_counts = frame['tz'].value_counts()
# 从USER_AGENT 数据中获取客户浏览器类型数据
results = pd.Series([x.split()[0] for x in frame.a.dropna()])
# 从USER_AGENT 数据中获取客户操作系统数据
cframe = frame[frame.a.notnull()]
operating_system = np.where(cframe['a'].str.contains('Windows'),'Windows','Not Windows')
# 将时区按照操作系统分组
by_tz_os = cframe.groupby(['tz',operating_system])
# 按分组计数,然后用 unstack重塑 数据
agg_counts = by_tz_os.size().unstack().fillna(0)
# 以按行加和并排序的数据作为索引
indexer = agg_counts.sum(1).argsort()
#用 take 函数, 通过索引取出时区最多的值
count_subset = agg_counts.take(indexer)[-10:]
建模和计算¶
暂无
结果展示¶
# TOP10 时区
tz_counts[:10].plot(kind='barh')
Out[6]:
# TOP10 时区的操作系统数量(表)
count_subset
Out[7]:
# TOP10 时区的操作系统数量(图)
count_subset.plot(kind = 'barh',stacked = True)
Out[8]:
# TOP10 时区的操作系统比例(图)
normed_subset = count_subset.div(count_subset.sum(1),axis = 0)
normed_subset.plot(kind = 'barh',stacked = True)
Out[9]:
例子:电影评分数据分析
%pylab inline
import pandas as pd
将数据加载到 DataFrame¶
# 定义列名
unames = ['user_id','gender','age','occupation','zip']
rnames = ['user_id','movie_id','rating','timestamp']
mnames = ['movie_id','title','genres']
# 读取数据
users = pd.read_table('../data/datasets/movielens/users.dat',sep='::',header=None,names=unames,engine='python')
ratings = pd.read_table('../data/datasets/movielens/ratings.dat',sep='::',header=None,names=rnames,engine='python')
movies = pd.read_table('../data/datasets/movielens/movies.dat',sep='::',header=None,names=mnames,engine='python')
# 检验数据
users.head()
ratings.head()
movies.head()
Out[7]:
合并数据¶
# 先合并 users 到ratings, 再合并movies
# pandas 会根据列名进行连接
data = pd.merge(pd.merge(ratings,users),movies)
data.head()
Out[8]:
数据转换¶
# 按性别统计每部电影的得分
# pivot_table, 透视表, 是一个很常用的工具
mean_ratings = data.pivot_table('rating', index='title',columns=['gender'], aggfunc='mean')
# 另一种写法
#mean_ratings = pd.pivot_table(data,values=['rating'], index='title',columns=['gender'], aggfunc='mean')
mean_ratings.head()
Out[9]:
数据筛选¶
# 筛选评分数据>250 条的电影
ratings_by_title = data.groupby('title').size() # 按照title对data分组并计数
active_titles = ratings_by_title.index[ratings_by_title >= 251] # index返回的下标
mean_ratings = mean_ratings.ix[active_titles] # 进行筛选。注意,这里原表和透视表的排序相同
mean_ratings.head()
Out[10]:
结果展示¶
# 女性观众最喜欢的 TOP10 电影
top_female_ratings = mean_ratings.sort_values(by='F',ascending=False)[:10]
top_female_ratings
Out[11]:
# 分歧最大的电影
mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F']
# 分歧最大且女性更喜欢的电影
mean_ratings.sort_values(by='diff')[:10]
Out[12]:
# 分歧最大且男性更喜欢的电影
mean_ratings.sort_values(by='diff',ascending=False)[:10]
Out[13]:
# 分歧最大的电影
rating_std_by_title = data.groupby('title')['rating'].std()
rating_std_by_title.sort_values(ascending=False)[:10]
Out[14]:
例子:全美婴儿姓名分析
%pylab inline
import pandas as pd
将数据加载到 DataFrame,并合并¶
#pd.read_csv('data/ch02/names/yob1880.txt', names=['name', 'sex', 'births'])
years = range(1880, 2011)
pieces = []
columns = ['name', 'sex', 'births']
for year in years:
path = '../data/datasets/babynames/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)
聚合¶
# 按年度聚合,区分性别
total_births = names.pivot_table('births', index='year',
columns='sex', aggfunc=sum)
total_births.head(10)
Out[4]:
# 画张图看看
total_births.plot(title='Total births by sex and year')
Out[5]:
数据转换¶
# 计算名字占所有出生婴儿的比重
def add_prop(group):
# Integer division floors
births = group.births.astype(float)
group['prop'] = births / births.sum()
return group
names = names.groupby(['year', 'sex']).apply(add_prop)
names.head()
Out[6]:
# 检查所有分组的 prop 之和是否近似=1
np.allclose(names.groupby(['year','sex']).prop.sum(),1)
Out[7]:
数据筛选¶
取 Top1000的名字
def get_top1000(group):
return group.sort_values(by='births', ascending=False)[:1000]
grouped = names.groupby(['year', 'sex'])
top1000 = grouped.apply(get_top1000)
top1000.head()
Out[8]:
boys = top1000[top1000.sex == 'M']
girls = top1000[top1000.sex == 'F']
# 按名字聚合,做透视表
total_births = top1000.pivot_table('births', index='year', columns='name',
aggfunc=sum)
# 分析5个名字的趋势
subset = total_births[['John', 'Harry', 'Mary', 'Marilyn']]
subset.plot(subplots=True, figsize=(12, 10), grid=False,
title="Number of births per year")
Out[9]:
评估名字的多样性¶
# 最流行的1000个名字的占比
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[10]:
# 占总出生人数前50%的不同名字的数量
def get_quantile_count(group, q=0.5):
group = group.sort_values(by='prop', ascending=False)
return group.prop.cumsum().values.searchsorted(q) + 1 #numpy 的 searchsorted,查找 q 放在哪个索引合适
diversity = top1000.groupby(['year', 'sex']).apply(get_quantile_count) # 按 year 分组,每个分组都执行一次
diversity = diversity.unstack('sex')
diversity.head()
Out[11]:
diversity.plot(title="Number of popular names in top 50%")
Out[12]:
最后一个字母的变革¶
# 获取尾字母,并聚合
get_last_letter = lambda x: x[-1]
last_letters = names.name.map(get_last_letter)
last_letters.name = 'last_letter'
table = names.pivot_table('births', index=last_letters,
columns=['sex', 'year'], aggfunc=sum)
# 选取有代表性的3个年份
subtable = table.reindex(columns=[1910, 1960, 2010], level='year')
subtable.head()
Out[13]:
# 规范化处理,计算比例
letter_prop = subtable / subtable.sum().astype(float)
# 绘图
plt.subplots_adjust(hspace=0.25)
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
letter_prop['M'].plot(kind='bar', rot=0, ax=axes[0], title='Male')
letter_prop['F'].plot(kind='bar', rot=0, ax=axes[1], title='Female',
legend=False)
Out[14]:
# 进一步分析男孩的 d, n, y 三个字母
letter_prop = table / table.sum().astype(float)
dny_ts = letter_prop.ix[['d', 'n', 'y'], 'M'].T
plt.close('all')
dny_ts.plot()
Out[15]:
男孩名变成女孩名¶
# 合并重复的名字
all_names = top1000.name.unique()
# 找到 lesl 开头的名字
mask = np.array(['lesl' in x.lower() for x in all_names])
lesley_like = all_names[mask]
lesley_like
Out[16]:
filtered = top1000[top1000.name.isin(lesley_like)]
filtered.groupby('name').births.sum()
Out[17]:
table = filtered.pivot_table('births', index='year',
columns='sex', aggfunc='sum')
table = table.div(table.sum(1), axis=0)
table.tail()
Out[18]:
plt.close('all')
table.plot(style={'M': 'k-', 'F': 'k--'})
Out[19]: