数据分析案例(USA.gov)
来自Bitly的USA.gov数据
2011年,URL缩短服务Bitly跟美国政府网站USA.gov合作,提供了一份从生成.gov或.mil短链接的用户那里收集来的匿名数据。
以每小时快照为例,文件中各行的格式为JSON:

使用json模块及其loads函数逐行加载已经下载好的数据文件
import json
records = [json.loads(line) for line in open(path)]

用纯Python代码对时区进行计数
求该数据集中常出现的是哪个时区(即tz字段)
time_zones = [rec['tz'] for rec in records]

因为并不是所有记录都有时区字段
time_zones = [rec['tz'] for rec in records if 'tz' in rec]

对时区进行计数,这里介绍两个办法:一个较难(只使用标准Python库),另一个较简单(使用pandas)
计数的办法之一是在遍历时区的过程中将计数值保存在字典中
def get_counts(sequence):
counts = {}
for x in sequence:
if x in counts:
counts[x] += 1
else:
counts[x] = 1
return counts
from collections import defaultdict
def get_counts2(sequence):
counts = defaultdict(int) # values will initialize to 0
for x in sequence:
counts[x] += 1
return counts
defaultdict
t的作用是在于,当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值(返回的是工厂函数的默认值,比如list对应[ ],str对应的是空字符串,set对应set( ),int对应0)

要得到前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:]

collections.Counter类,它可以使这项工作更简单
from collections import Counter
counts = Counter(time_zones)
counts.most_common(10)

用pandas对时区进行计数
import pandas as pd
frame = pd.DataFrame(records)
frame.info()


对Series使用value_counts方法
tz_counts = frame['tz'].value_counts()

可以用matplotlib可视化这个数据。为此,我们先给记录中未知或缺失的时区填上一个替代值。fillna函数可以替换缺失值(NA),而未知值(空字符串)则可以通过布尔型数组索引加以替换
clean_tz = frame['tz'].fillna('Missing')
clean_tz[clean_tz == ''] = 'Unknown'
tz_counts = clean_tz.value_counts()

用seaborn包创建水平柱状图
import seaborn as sns
subset = tz_counts[:10]
sns.barplot(y=subset.index, x=subset.values)

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

将这些”agent”字符串中的所有信息都解析出来是一件挺郁闷的工作。一种策略是将这种字符串的第一节(与浏览器大致对应)分离出来并得到另外一份用户行为摘要
results = pd.Series([x.split()[0] for x in frame.a.dropna()])

假设想按Windows和非Windows用户对时区统计信息进行分解。为了简单起见,我们假定只要agent字符串中含有”Windows”就认为该用户为Windows用户。由于有的agent缺失,所以首先将它们从数据中移除
cframe = frame[frame.a.notnull()]
然后计算出各行是否含有Windows的值
cframe['os'] = np.where(cframe['a'].str.contains('Windows'), 'Windows', 'Not Windows')

根据时区和新得到的操作系统列表对数据进行分组
by_tz_os = cframe.groupby(['tz', 'os'])
分组计数,类似于value_counts函数,可以用size来计算。并利用unstack对计数结果进行重塑
agg_counts = by_tz_os.size().unstack().fillna(0)

根据agg_counts中的行数构造了一个间接索引数组
indexer = agg_counts.sum(1).argsort()

通过take按照这个顺序截取了后10行大值
count_subset = agg_counts.take(indexer[-10:])

pandas有一个简便方法nlargest,可以做同样的工作

传递一个额外参数到seaborn的barpolt函数,来画一个堆积条形图
count_subset = count_subset.stack()
count_subset.name = 'total'
count_subset = count_subset.reset_index()
count_subset[:10]

这张图不容易看出Windows用户在小分组中的相对比例,因此标准化分组百分比之和为1
def norm_total(group):
group['normed_total'] = group.total / group.total.sum()
return group
results = count_subset.groupby('tz').apply(norm_total)
sns.barplot(x='normed_total', y='tz', hue='os', data=results)

还可以用groupby的transform方法,更高效的计算标准化的和
g = count_subset.groupby('tz')
results2 = count_subset.total / g.total.transform('sum')