Pandas进阶 期中练习
pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充
【任务一】企业收入的多样性
【题目描述】一个企业的产业收入多样性可以仿照信息熵的概念来定义收入熵指标:
I
=
−
∑
i
p
(
x
i
)
log
(
p
(
x
i
)
)
\mathrm{I}=-\sum_{\mathrm{i}} \mathrm{p}\left(\mathrm{x}_{\mathrm{i}}\right) \log \left(\mathrm{p}\left(\mathrm{x}_{\mathrm{i}}\right)\right)
I=−i∑p(xi)log(p(xi))
其中 p(xi)
是企业该年某产业收入额占该年所有产业总收入的比重。在company.csv中存有需要计算的企业和年份,在company_data.csv中存有企业、各类收入额和收入年份的信息。现请利用后一张表中的数据,在前一张表中增加一列表示该公司该年份的收入熵指标
I。
【数据下载】链接:https://pan.baidu.com/s/1leZZctxMUSW55kZY5WwgIw 密码:u6fd
【我的思路】
按公司和日期分组,求出收入额与总额的比值,并利用比值求每个项的熵再求出每个组的增益,然后按公司和日期将两表合并
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
df1 = pd.read_csv('../data/Company_1.csv')
df2 = pd.read_csv('../data/company_data.csv')
先看一下两个数据集的结构
df1.head()
证券代码 | 日期 | |
---|---|---|
0 | #000007 | 2014 |
1 | #000403 | 2015 |
2 | #000408 | 2016 |
3 | #000408 | 2017 |
4 | #000426 | 2015 |
df2.head()
证券代码 | 日期 | 收入类型 | 收入额 | |
---|---|---|---|---|
0 | 1 | 2008/12/31 | 1 | 1.084218e+10 |
1 | 1 | 2008/12/31 | 2 | 1.259789e+10 |
2 | 1 | 2008/12/31 | 3 | 1.451312e+10 |
3 | 1 | 2008/12/31 | 4 | 1.063843e+09 |
4 | 1 | 2008/12/31 | 5 | 8.513880e+08 |
结果是要求每个公司每年的收入熵,因此应该考虑在收入表中按企业和年份分组
查看一下分组后有没有出现收入类型重复的情况,因为如果重复的话应该把同类型的收入求和
tmp = df2[['证券代码', '日期', '收入类型']]
tmp.drop_duplicates().equals(tmp)
True
结果表明没有重复
然后按照公式一步一步求,p是每个公司每日期的比例,因此先求出每个小组的收入额的和,再求p,根据p和log p求出每一项的熵,然后求和就是每个组的信息增益,这里因为有0值,求log以后会变成无限大,无限大与0相乘后会变成空值,所以需要对空值处理一下使其为0
df2['total'] = df2.groupby(['证券代码', '日期'])['收入额'].transform('sum')
df2['ratio'] = df2['收入额'] / df2['total']
df2['entropy'] = df2['ratio']*np.log(df2['ratio'])
df2['entropy'].fillna(0, inplace=True)
df2['I'] = -df2.groupby(['证券代码', '日期'])['entropy'].transform('sum')
df2.tail()
证券代码 | 日期 | 收入类型 | 收入额 | total | ratio | entropy | I | |
---|---|---|---|---|---|---|---|---|
964017 | 900957 | 2016/12/31 | 12 | 0.00 | 6.399964e+08 | 0.000000 | 0.000000 | 2.178318 |
964018 | 900957 | 2016/12/31 | 13 | 0.00 | 6.399964e+08 | 0.000000 | 0.000000 | 2.178318 |
964019 | 900957 | 2016/12/31 | 14 | 52072238.97 | 6.399964e+08 | 0.081363 | -0.204127 | 2.178318 |
964020 | 900957 | 2016/12/31 | 15 | 0.00 | 6.399964e+08 | 0.000000 | 0.000000 | 2.178318 |
964021 | 900957 | 2016/12/31 | 16 | 52072238.97 | 6.399964e+08 | 0.081363 | -0.204127 | 2.178318 |
res = df2[['证券代码', '日期', 'I']].drop_duplicates()
res.head()
证券代码 | 日期 | I | |
---|---|---|---|
0 | 1 | 2008/12/31 | 2.085159 |
14 | 1 | 2009/12/31 | 1.671752 |
22 | 1 | 2010/12/31 | 2.108355 |
34 | 1 | 2011/12/31 | 3.150479 |
72 | 1 | 2012/12/31 | 2.718759 |
由于证券代码和日期的格式对不上,所以更改一下格式
res['证券代码'] = res['证券代码'].apply(lambda x: '#' + (6-len(str(x)))*'0' + str(x))
res['日期'] = res['日期'].apply(lambda x: int(str(x)[:4]))
res.head()
证券代码 | 日期 | I | |
---|---|---|---|
0 | #000001 | 2008 | 2.085159 |
14 | #000001 | 2009 | 1.671752 |
22 | #000001 | 2010 | 2.108355 |
34 | #000001 | 2011 | 3.150479 |
72 | #000001 | 2012 | 2.718759 |
这里发现merge的时候需要保持两个表中的列的类型是一样的,原本在公司表中日期是int类型,而公司数据表中日期是object类型,因此在上一步做数据转换的时候要把类型也显式转换一下
最后再按照原表的顺序求信息增益
df1 = df1.merge(res, on=['证券代码', '日期'])
df1.head()
证券代码 | 日期 | I | |
---|---|---|---|
0 | #000007 | 2014 | 3.070462 |
1 | #000403 | 2015 | 2.790585 |
2 | #000408 | 2016 | 2.818541 |
3 | #000426 | 2015 | 3.084266 |
4 | #000426 | 2016 | 2.988900 |
【使用场景】
本道题中提出的信息增益是决策树中常用的算法,用来求结点分裂时的最优分裂特征,最优分裂特征应当是信息增益(比)最高的,类似信息增益作用的算法还有基尼指数,基尼指数相对于信息增益比的优势是其计算更简便,不涉及log计算,而且是二叉树,优化了计算速度,在效率上由于信息增益比,在效果上是信息增益比的一种近似代替。
这里我根据这道题的数据再计算一下基尼指数,并分别计算一下两种算法的效率
基尼指数的公式:
Gini
(
D
)
=
∑
i
=
1
n
p
(
x
i
)
∗
(
1
−
p
(
x
i
)
)
=
1
−
∑
i
=
1
n
p
(
x
i
)
2
\begin{aligned} \operatorname{Gini}(D) &=\sum_{i=1}^{n} p\left(x_{i}\right) *\left(1-p\left(x_{i}\right)\right) &=1-\sum_{i=1}^{n} p\left(x_{i}\right)^{2} \end{aligned}
Gini(D)=i=1∑np(xi)∗(1−p(xi))=1−i=1∑np(xi)2
df2['ratio_2'] = df2['ratio'] * df2['ratio']
df2 = df2.groupby(['证券代码', '日期'])['ratio_2'].sum().reset_index()
df2.head()
证券代码 | 日期 | ratio_2 | |
---|---|---|---|
0 | 1 | 2008/12/31 | 0.148788 |
1 | 1 | 2009/12/31 | 0.230552 |
2 | 1 | 2010/12/31 | 0.139320 |
3 | 1 | 2011/12/31 | 0.057870 |
4 | 1 | 2012/12/31 | 0.086869 |
df2['gini'] = 1 - df2['ratio_2']
df2['证券代码'] = df2['证券代码'].apply(lambda x: '#' + (6-len(str(x)))*'0' + str(x))
df2['日期'] = df2['日期'].apply(lambda x: int(str(x)[:4]))
df2 = df2[['证券代码', '日期', 'gini']]
df2.head()
证券代码 | 日期 | gini | |
---|---|---|---|
0 | #000001 | 2008 | 0.851212 |
1 | #000001 | 2009 | 0.769448 |
2 | #000001 | 2010 | 0.860680 |
3 | #000001 | 2011 | 0.942130 |
4 | #000001 | 2012 | 0.913131 |
res = res.merge(df2, on=['证券代码', '日期'])
res.head()
证券代码 | 日期 | I | gini | |
---|---|---|---|---|
0 | #000001 | 2008 | 2.085159 | 0.851212 |
1 | #000001 | 2009 | 1.671752 | 0.769448 |
2 | #000001 | 2010 | 2.108355 | 0.860680 |
3 | #000001 | 2011 | 3.150479 | 0.942130 |
4 | #000001 | 2012 | 2.718759 | 0.913131 |
import seaborn as sns
sns.distplot(res['I'])
sns.distplot(res['gini'])
两个好像大致分布是一样的吧0.0
不过可以看出gini的结果更为平滑一些
再比较一下计算效率
df2 = pd.read_csv('../data/company_data.csv')
df2['total'] = df2.groupby(['证券代码', '日期'])['收入额'].transform('sum')
df2['ratio'] = df2['收入额'] / df2['total']
%%timeit
df2['entropy'] = df2['ratio']*np.log(df2['ratio'])
df2['I'] = -df2.groupby(['证券代码', '日期'])['entropy'].transform('sum')
151 ms ± 25.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
df2 = pd.read_csv('../data/company_data.csv')
df2['total'] = df2.groupby(['证券代码', '日期'])['收入额'].transform('sum')
df2['ratio'] = df2['收入额'] / df2['total']
df2.head()
%%timeit
df2['ratio_2'] = df2['ratio'] * df2['ratio']
df3 = df2.groupby(['证券代码', '日期'])['ratio_2'].sum().reset_index()
df3['gini'] = 1 - df3['ratio_2']
124 ms ± 21.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
两段代码里都包含了一个groupby的操作,因此计算不是很精确,不过还是可以看出基尼指数的计算速度相较于信息增益快了20%左右
【第二题】组队学习信息表的变换
【题目描述】请把组队学习的队伍信息表变换为如下形态,其中“是否队长”一列取1表示队长,否则为0
数据非公开,这里不提供
【我的思路】这是个宽表变长表的操作,应该是可以用wide_to_long进行操作的,但还没想好wide_to_long怎么操作。
目前我的思路是先提取出所有队长的列,然后把每个队员的列的数据按列插入到队长表中
ps.后记:提交完之后我看到了Gocara大佬的答案,明白了wide_to_long的做法,这里我在我的第一版内容之后又加了wide_to_long的做法,并比对一下两种做法的效率
teams = pd.read_excel('../data/team_info.xlsx', engine='openpyxl')
teams.head()
所在群 | 队伍名称 | 队长编号 | 队长_群昵称 | 队员1 编号 | 队员_群昵称 | 队员2 编号 | 队员_群昵称.1 | 队员3 编号 | 队员_群昵称.2 | ... | 队员6 编号 | 队员_群昵称.5 | 队员7 编号 | 队员_群昵称.6 | 队员8 编号 | 队员_群昵称.7 | 队员9 编号 | 队员_群昵称.8 | 队员10编号 | 队员_群昵称.9 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Pandas数据分析 | 你说的都对队 | 5.0 | 山枫叶纷飞 | 6.0 | 蔡 | 7.0 | 安慕希 | 8.0 | 信仰 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | Pandas数据分析 | 熊猫人 | 175.0 | 鱼呲呲 | 44.0 | Heaven | 37.0 | 吕青 | 50.0 | 余柳成荫 | ... | 25.0 | Never say never | 55.0 | K | 120.0 | Y. | 28.0 | X.Y.Q | 151.0 | swrong |
2 | Pandas数据分析 | 中国移不动 | 107.0 | Y's | 124.0 | 🥕 | 75.0 | Vito | 146.0 | 张小五 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | Pandas数据分析 | panda | 11.0 | 太下真君 | 35.0 | 柚子 | 108.0 | My | 42.0 | 星星点灯 | ... | 157.0 | Zys | 158.0 | 不器 | 102.0 | 嘉平佑染 | NaN | NaN | NaN | NaN |
4 | Pandas数据分析 | 一路向北 | 13.0 | 黄元帅 | 15.0 | 化 | 16.0 | 未期 | 18.0 | 太陽光下 | ... | 23.0 | 🚀 | 169.0 | 听风 | 189.0 | Cappuccino | NaN | NaN | NaN | NaN |
5 rows × 24 columns
看这个表的结构和结果集的结构,想应该可以用宽表变长表的方法做,但是没想好应该怎么弄,于是用了一种朴素的方法o.o
每列每列的填充上去
cols = list(teams.columns)
res = teams[cols[1:4]]
res['是否队长'] = 1
res.tail(2)
队伍名称 | 队长编号 | 队长_群昵称 | 是否队长 | |
---|---|---|---|---|
20 | 应如是 | 54.0 | 思无邪 | 1 |
21 | NaN | NaN | NaN | 1 |
观察给出的数据的列除了前4列之外,后面的都是每两列表示一个编号和昵称,所以就循环加入到结果集中
res = res.rename(columns={'队长编号':'编号', '队长_群昵称':'昵称'})
res.head(2)
队伍名称 | 编号 | 昵称 | 是否队长 | |
---|---|---|---|---|
0 | 你说的都对队 | 5.0 | 山枫叶纷飞 | 1 |
1 | 熊猫人 | 175.0 | 鱼呲呲 | 1 |
for i in range(4,24,2):
c = [cols[i], cols[i+1], cols[1]]
tmp = teams[c]
tmp.columns = ['昵称','编号','队伍名称']
res = pd.concat([res, tmp])
res['是否队长'].fillna(0, inplace=True)
res.head()
队伍名称 | 编号 | 昵称 | 是否队长 | |
---|---|---|---|---|
0 | 你说的都对队 | 5 | 山枫叶纷飞 | 1.0 |
1 | 熊猫人 | 175 | 鱼呲呲 | 1.0 |
2 | 中国移不动 | 107 | Y's | 1.0 |
3 | panda | 11 | 太下真君 | 1.0 |
4 | 一路向北 | 13 | 黄元帅 | 1.0 |
res = res.dropna()
res = res[['是否队长', '队伍名称', '昵称', '编号']]
res.tail()
是否队长 | 队伍名称 | 昵称 | 编号 | |
---|---|---|---|---|
1 | 0.0 | 熊猫人 | 28 | X.Y.Q |
6 | 0.0 | Why—贰肆 | 1 | MoXQian |
7 | 0.0 | 师承潘大师队 | 159 | 遇安 |
12 | 0.0 | 没想好叫什么队 | 117 | xxxxxxl |
1 | 0.0 | 熊猫人 | 151 | swrong |
res.shape
(146, 4)
最后得出的数据中共有146人
【wide_to_long】做法,这部分我自己一开始没想出来,参考了别人的思路,自己实现了一下,我主要比较一下wide_to_long和我自己实现的concat算法的效率
df = pd.read_excel('../data/team_info.xlsx', engine='openpyxl')
df.drop(columns=['所在群'], inplace=True)
df.head(2)
队伍名称 | 队长编号 | 队长_群昵称 | 队员1 编号 | 队员_群昵称 | 队员2 编号 | 队员_群昵称.1 | 队员3 编号 | 队员_群昵称.2 | 队员4 编号 | ... | 队员6 编号 | 队员_群昵称.5 | 队员7 编号 | 队员_群昵称.6 | 队员8 编号 | 队员_群昵称.7 | 队员9 编号 | 队员_群昵称.8 | 队员10编号 | 队员_群昵称.9 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 你说的都对队 | 5.0 | 山枫叶纷飞 | 6.0 | 蔡 | 7.0 | 安慕希 | 8.0 | 信仰 | 20.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 熊猫人 | 175.0 | 鱼呲呲 | 44.0 | Heaven | 37.0 | 吕青 | 50.0 | 余柳成荫 | 82.0 | ... | 25.0 | Never say never | 55.0 | K | 120.0 | Y. | 28.0 | X.Y.Q | 151.0 | swrong |
2 rows × 23 columns
cols = ['队伍名称', '编号_队长', '昵称_队长']
for i in range(10):
cols += [f'编号_队员{i}', f'昵称_队员{i}']
df.columns = cols
df.head(2)
队伍名称 | 编号_队长 | 昵称_队长 | 编号_队员0 | 昵称_队员0 | 编号_队员1 | 昵称_队员1 | 编号_队员2 | 昵称_队员2 | 编号_队员3 | ... | 编号_队员5 | 昵称_队员5 | 编号_队员6 | 昵称_队员6 | 编号_队员7 | 昵称_队员7 | 编号_队员8 | 昵称_队员8 | 编号_队员9 | 昵称_队员9 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 你说的都对队 | 5.0 | 山枫叶纷飞 | 6.0 | 蔡 | 7.0 | 安慕希 | 8.0 | 信仰 | 20.0 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 熊猫人 | 175.0 | 鱼呲呲 | 44.0 | Heaven | 37.0 | 吕青 | 50.0 | 余柳成荫 | 82.0 | ... | 25.0 | Never say never | 55.0 | K | 120.0 | Y. | 28.0 | X.Y.Q | 151.0 | swrong |
2 rows × 23 columns
res = pd.wide_to_long(df,
stubnames=['编号', '昵称'],
i = ['队伍名称'],
j='是否队长',
sep='_',
suffix='.+')
res = res.reset_index()
res['是否队长'] = res['是否队长'].map({'队长':1}).fillna(0)
res = res.dropna()
res = res[['是否队长', '队伍名称', '昵称', '编号']]
res.head()
是否队长 | 队伍名称 | 昵称 | 编号 | |
---|---|---|---|---|
0 | 1.0 | 你说的都对队 | 山枫叶纷飞 | 5.0 |
1 | 1.0 | 熊猫人 | 鱼呲呲 | 175.0 |
2 | 1.0 | 中国移不动 | Y's | 107.0 |
3 | 1.0 | panda | 太下真君 | 11.0 |
4 | 1.0 | 一路向北 | 黄元帅 | 13.0 |
res.shape
(146, 4)
以上是wide_to_long的实现算法,可以看出结果是一致的
比较一下两种做法的效率:
teams = pd.read_excel('../data/team_info.xlsx', engine='openpyxl')
cols = list(teams.columns)
%%timeit
res = teams[cols[1:4]]
res['是否队长'] = 1
res = res.rename(columns={'队长编号':'编号', '队长_群昵称':'昵称'})
for i in range(4,24,2):
c = [cols[i], cols[i+1], cols[1]]
tmp = teams[c]
tmp.columns = ['昵称','编号','队伍名称']
res = pd.concat([res, tmp])
res['是否队长'].fillna(0, inplace=True)
res = res.dropna()
32.5 ms ± 3.21 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
teams = pd.read_excel('../data/team_info.xlsx', engine='openpyxl')
cols = ['队伍名称', '编号_队长', '昵称_队长']
for i in range(10):
cols += [f'编号_队员{i}', f'昵称_队员{i}']
%%timeit
df = teams.drop(columns=['所在群'])
df.columns = cols
%timeit
res = pd.wide_to_long(df,
stubnames=['编号', '昵称'],
i = ['队伍名称'],
j='是否队长',
sep='_',
suffix='.+')
res = res.reset_index()
res['是否队长'] = res['是否队长'].map({'队长':1}).fillna(0)
res = res.dropna()
25.5 ms ± 4.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
两种做法都需要一些预处理和后处理,把这些处理步骤一同考虑进去,可以看出wide_to_long比concat的做法快了20%以上
【第三题】投票情况
【题目描述】两张数据表中分别给出了county的人口数以及大选的投票情况,请解决以下问题:
- 有多少县满足总投票数超过县人口数的一半
- 把州(state)作为行索引,把投票候选人作为列名,列名的顺序按照候选人总票数由高到低排序,行列对应的元素为该候选人在该州获得的总票数
- 求BT state
d1 = pd.read_csv('../data/county_population.csv')
d2 = pd.read_csv('../data/president_county_candidate.csv')
d1.head()
US County | Population | |
---|---|---|
0 | .Autauga County, Alabama | 55869 |
1 | .Baldwin County, Alabama | 223234 |
2 | .Barbour County, Alabama | 24686 |
3 | .Bibb County, Alabama | 22394 |
4 | .Blount County, Alabama | 57826 |
d2.head()
state | county | candidate | party | total_votes | won | |
---|---|---|---|---|---|---|
0 | Delaware | Kent County | Joe Biden | DEM | 44552 | True |
1 | Delaware | Kent County | Donald Trump | REP | 41009 | False |
2 | Delaware | Kent County | Jo Jorgensen | LIB | 1044 | False |
3 | Delaware | Kent County | Howie Hawkins | GRN | 420 | False |
4 | Delaware | New Castle County | Joe Biden | DEM | 195034 | True |
第一问
思路: 先求每个县的投票总人数,然后把人口表构造成州、县的形式merge以后求解
total_votes = d2.groupby(['state', 'county'])['total_votes'].sum()
pop = d1.copy()
pop['state'] = pop['US County'].apply(lambda x: str(x).split(', ')[1])
pop['county'] = pop['US County'].apply(lambda x: str(x).split(', ')[0][1:])
pop = pop.set_index(['state', 'county'])
pop.head()
US County | Population | ||
---|---|---|---|
state | county | ||
Alabama | Autauga County | .Autauga County, Alabama | 55869 |
Baldwin County | .Baldwin County, Alabama | 223234 | |
Barbour County | .Barbour County, Alabama | 24686 | |
Bibb County | .Bibb County, Alabama | 22394 | |
Blount County | .Blount County, Alabama | 57826 |
pop = pop.join(total_votes)
pop['ratio'] = pop['total_votes'] / pop['Population']
res1 = pop[pop['ratio']>0.5]
res1.head()
US County | Population | total_votes | ratio | ||
---|---|---|---|---|---|
state | county | ||||
Alabama | Choctaw County | .Choctaw County, Alabama | 12589 | 7464.0 | 0.592899 |
Clarke County | .Clarke County, Alabama | 23622 | 13135.0 | 0.556049 | |
Clay County | .Clay County, Alabama | 13235 | 6930.0 | 0.523612 | |
Colbert County | .Colbert County, Alabama | 55241 | 27886.0 | 0.504806 | |
Conecuh County | .Conecuh County, Alabama | 12067 | 6441.0 | 0.533770 |
res1.shape
(1434, 4)
共有1434个县投票人数超过0.5
第二问
分析目标表和原表,认为这是个长变宽的任务,所以考虑用pivot来实现,索引列是state, 值是total_votes,列是candidate,不过由于一个州有多个县有候选人的投票,所以应该先按州和候选人分组求和,再长变宽
d2.head()
state | candidate | total_votes | |
---|---|---|---|
0 | Alabama | Write-ins | 7312 |
1 | Alabama | Donald Trump | 1441168 |
2 | Alabama | Jo Jorgensen | 25176 |
3 | Alabama | Joe Biden | 849648 |
4 | Alaska | Write-ins | 34210 |
#选择需要做运算的列
df = d2[['state', 'candidate', 'total_votes']]
#先求出候选人的票数排名
df = df.groupby('candidate')['total_votes'].sum().sort_values(ascending=False)
cols = list(df.index)
d2 = d2.groupby(['state', 'candidate'])['total_votes'].sum().reset_index()
res = d2.pivot(index='state',
columns='candidate',
values='total_votes')
res.head()
candidate | None of these candidates | Write-ins | Alyson Kennedy | Bill Hammons | Blake Huber | Brian Carroll | Brock Pierce | Brooke Paige | Christopher LaFontaine | Connie Gammon | ... | Mark Charles | Phil Collins | President Boddie | Princess Jacob-Fambro | Richard Duncan | Ricki Sue King | Rocky De La Fuente | Sheila Samm Tittle | Tom Hoefling | Zachary Scalf |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
state | |||||||||||||||||||||
Alabama | NaN | 7312.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Alaska | NaN | 34210.0 | NaN | NaN | NaN | NaN | 825.0 | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | 318.0 | NaN | NaN | NaN |
Arizona | NaN | 2032.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Arkansas | NaN | NaN | NaN | NaN | NaN | 1713.0 | 2141.0 | NaN | NaN | 1475.0 | ... | NaN | 2812.0 | NaN | NaN | NaN | NaN | 1321.0 | NaN | NaN | NaN |
California | NaN | 80.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | 60155.0 | NaN | NaN | NaN |
5 rows × 38 columns
这里发现了两个比较怪的候选人姓名:None of these candidates
和Write-ins
前者比较好理解,后者我查了一下,表示的含义是填选票的时候没有填已有的候选人,而是自己填了某个名字作为候选人
然后我进一步看了一下所有数据,只有Nevada
州在None of these candidates
列有值,而且Nevada在Write-ins
列是空值,因此根据这两列的语义,有理由认为这两列表示的是一个意思,只不过Nevada州的说法不一样。
所以我将原数据集更改了一下,吧Nevada的None of these candidates列的值填到Write-ins列并删除了None of these candidates列。
res.loc['Nevada'][' Write-ins'] = res.loc['Nevada'][' None of these candidates']
res.drop(columns=[' None of these candidates'], inplace=True)
#删了这一列之后别忘了删掉前面排好序的列名
del cols[cols.index(' None of these candidates')]
最后一步:调整列顺序,填充值
res = res[cols]
res.fillna(0, inplace=True)
res.head()
candidate | Joe Biden | Donald Trump | Jo Jorgensen | Howie Hawkins | Write-ins | Rocky De La Fuente | Gloria La Riva | Kanye West | Don Blankenship | Brock Pierce | ... | Tom Hoefling | Ricki Sue King | Princess Jacob-Fambro | Blake Huber | Richard Duncan | Joseph Kishore | Jordan Scott | Gary Swing | Keith McCormic | Zachary Scalf |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
state | |||||||||||||||||||||
Alabama | 849648.0 | 1441168.0 | 25176.0 | 0.0 | 7312.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Alaska | 153405.0 | 189892.0 | 8896.0 | 0.0 | 34210.0 | 318.0 | 0.0 | 0.0 | 1127.0 | 825.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Arizona | 1672143.0 | 1661686.0 | 51465.0 | 0.0 | 2032.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Arkansas | 423932.0 | 760647.0 | 13133.0 | 2980.0 | 0.0 | 1321.0 | 1336.0 | 4099.0 | 2108.0 | 2141.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
California | 11109764.0 | 6005961.0 | 187885.0 | 81025.0 | 80.0 | 60155.0 | 51036.0 | 0.0 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
5 rows × 37 columns
第三问
这里我将得票率定义为候选人在某州得票数与该州总人数的比值
第一步先求出bt值
求出candidate为b和t的项,缩减运算规模
d2 = pd.read_csv('../data/president_county_candidate.csv')
d2 = d2[(d2['candidate']=='Joe Biden') | (d2['candidate']=='Donald Trump')]
d2.head()
state | county | candidate | party | total_votes | won | |
---|---|---|---|---|---|---|
0 | Delaware | Kent County | Joe Biden | DEM | 44552 | True |
1 | Delaware | Kent County | Donald Trump | REP | 41009 | False |
4 | Delaware | New Castle County | Joe Biden | DEM | 195034 | True |
5 | Delaware | New Castle County | Donald Trump | REP | 88364 | False |
8 | Delaware | Sussex County | Donald Trump | REP | 71230 | True |
pop = d1.copy()
pop['state'] = pop['US County'].apply(lambda x: str(x).split(', ')[1])
pop['county'] = pop['US County'].apply(lambda x: str(x).split(', ')[0][1:])
pop.head()
US County | Population | state | county | |
---|---|---|---|---|
0 | .Autauga County, Alabama | 55869 | Alabama | Autauga County |
1 | .Baldwin County, Alabama | 223234 | Alabama | Baldwin County |
2 | .Barbour County, Alabama | 24686 | Alabama | Barbour County |
3 | .Bibb County, Alabama | 22394 | Alabama | Bibb County |
4 | .Blount County, Alabama | 57826 | Alabama | Blount County |
d2 = d2.merge(pop, on=['state', 'county'])
d2 = d2[['state', 'county', 'candidate', 'total_votes', 'Population']]
d2['ratio'] = d2['total_votes'] / d2['Population']
d2.head()
state | county | candidate | total_votes | Population | ratio | |
---|---|---|---|---|---|---|
0 | Delaware | Kent County | Joe Biden | 44552 | 180786 | 0.246435 |
1 | Delaware | Kent County | Donald Trump | 41009 | 180786 | 0.226837 |
2 | Delaware | New Castle County | Joe Biden | 195034 | 558753 | 0.349052 |
3 | Delaware | New Castle County | Donald Trump | 88364 | 558753 | 0.158145 |
4 | Delaware | Sussex County | Donald Trump | 71230 | 234225 | 0.304109 |
# d3 = d2.set_index(['state', 'county'])
biden = d2[d2['candidate']=='Joe Biden']
trump = d2[d2['candidate']=='Donald Trump']
d3 = pd.merge(biden, trump, on=['state', 'county'])
d3['bt'] = d3['ratio_x'] - d3['ratio_y']
d3 = d3[['state', 'county', 'bt']]
d3.head()
state | county | bt | |
---|---|---|---|
0 | Delaware | Kent County | 0.019598 |
1 | Delaware | New Castle County | 0.190907 |
2 | Delaware | Sussex County | -0.062111 |
3 | District of Columbia | District of Columbia | 0.052874 |
4 | Florida | Alachua County | 0.143962 |
求每个州的中位数
d3 = d3.groupby('state')['bt'].median().reset_index()
d3[d3['bt']>0]['state']
3 California
5 Delaware
6 District of Columbia
9 Hawaii
25 New Jersey
Name: state, dtype: object
最后找出的州是这几个,怎么感觉有点少呀…
后记:这里我看到自己答案是五个,感觉和现实不太相符,所以询问了大家的答案,猜想是我对得票率的定义不同
上面的代码我的得票率的定义是候选人在某州得票数与该州总人数的比值
以下我再将得票率定义为候选人在某州得票数与该州总投票数的比值
重新计算一遍,除了比例这个地方的算法不一样,其他的地方和上面代码一样,所以就不多解释了
d2 = pd.read_csv('../data/president_county_candidate.csv')
res = d2.copy()
res['total'] = d2.groupby(['state', 'county'])['total_votes'].transform('sum')
res = res[(res['candidate']=='Joe Biden') | (res['candidate']=='Donald Trump')]
res['ratio'] = res['total_votes'] / res['total']
res = res[['state', 'county', 'candidate', 'ratio']]
biden = res[res['candidate']=='Joe Biden']
trump = res[res['candidate']=='Donald Trump']
d3 = pd.merge(biden, trump, on=['state', 'county'])
d3['bt'] = d3['ratio_x'] - d3['ratio_y']
d3 = d3[['state', 'county', 'bt']]
d3 = d3.groupby('state')['bt'].median().reset_index()
d3[d3['bt']>0]['state']
4 California
6 Connecticut
7 Delaware
8 District of Columbia
11 Hawaii
21 Massachusetts
30 New Jersey
39 Rhode Island
45 Vermont
Name: state, dtype: object
以新的得票率定义得出结果有9个州,看来算法没有问题,是对得票率的定义有歧义造成的,找到问题所在啦!
全部完成啦!!祝自己元旦快乐!也祝愿正在看这篇文章的你元旦快乐!愿2021如你所愿!