需求概述
现有美国各州数年人口普查的数据,需要进行如下分析操作:
- 导入文件,查看原始数据
- 将人口数据和各州简称数据进行合并
- 将合并的数据中重复的 abbreviation 列进行删除
- 查看存在缺失数据的列
- 找到有哪些 state/region 使得 state 的值为 NaN,进行去重操作
- 为找到的这些 state/region 的 state 项补上正确的值,从而去除掉 state 这一列的所有 NaN
- 合并各州面积数据 areas
- 我们会发现
area(sq.mi)
这一列有缺失数据,找出是哪些行 - 去除含有缺失数据的行
- 找出 2010 年的全民人口数据
- 计算各州的人口密度
- 排序,并找出人口密度最高的州
导入文件,查看原始数据
首先导入需要用到的各种模块:
1 | import numpy as np |
三个文件都是 csv 格式,直接读取即可。
首先读取的是各州简称数据 state-abbrevs.csv
:
1 | abbv = pd.read_csv('state-abbrevs.csv') |
前五行数据如下,数据中的 state 为州的全称,abbreviation 为州的简称:
1 | state abbreviation |
然后读取各州人口数据 state-population.csv
:
1 | pop = pd.read_csv('state-population.csv') |
前五行的数据如下,其中 state/region 为州的简称,ages 为年龄层次,year 为年份,population 为人口数量:
1 | state/region ages year population |
还有一个文件,各州面积数据 state-areas.csv
,需要读取:
1 | area = pd.read_csv('state-areas.csv') |
前五行数据如下,其中 state 是州的全称,area(sq.mi)
是州的面积:
1 | state area (sq. mi) |
将人口数据和各州简称数据进行合并
将人口数据和各州简称数据合并,只需要一条 merge 语句即可实现:
1 | abbv_pop = pd.merge(abbv, pop, left_on='abbreviation', right_on='state/region', how='outer') |
合并后的数据前五行为:
1 | state abbreviation state/region ages year population |
将合并的数据中重复的 abbreviation 列进行删除
新生成的数据表中,abbreviation 和 state/region 这两列实际上是相同的,都表示的是州名简称。我们可以将 abbreviation 列删除,只保留 state/region 即可:
1 | abbv_pop.drop('abbreviation', axis=1, inplace=True) |
删除掉重复列后的前五行数据为:
1 | state state/region ages year population |
查看存在缺失数据的列
数据表中行数很多,很难判断哪一列有空值。
我们可以通过 info 方法来判断某一列是否有空值:
1 | abbv_pop.info() |
其结果为:
1 | <class 'pandas.core.frame.DataFrame'> |
总共 2544 条数据,除了 state 和 population 的其他列都有 2544 个非空数据。state 列中只有 2448 条非空数据,population 中有 2524 条数据,说明这两列中是含有空值的。
我们也可以通过 isnull 和 any 结合使用的方式,检查哪一列中有空元素:
1 | abbv_pop.isnull().any(axis=0) |
返回的结果如下:
1 | state True |
值为 True,意味着改行有空值;值为 False,意味着改行没有空值。我们得到的结论仍然是,state 和 population 这两列中是有空值的,其他列中没有空值。
找到值为 NaN 的州对应的简称,并进行去重操作
找到有哪些 state/region 使得 state 的值为 NaN,将state中的空值对应的简称找到,对找到的简称进行去重操作。
首先,我们要找到 state 中为空值的那些行。方法很简单,通过 isnull 判断是否为空,然后将得到的布尔值作为索引,将所有的空值取到即可:
1 | abbv_pop.loc[abbv_pop['state'].isnull()] |
拿到的结果为:
1 | state state/region ages year population |
找到这些 state 为 NaN 的行之后,我们就可以找到它们对应的州名简称了:
1 | abbv_pop.loc[abbv_pop['state'].isnull()]['state/region'] |
这些州的简称为:
1 | 2448 PR |
我们看到,这里面有大量的重复的名称,需要稍微去重一下,使用 unique 方法即可:
1 | abbv_pop.loc[abbv_pop['state'].isnull()]['state/region'].unique() |
这样,就拿到了州名为 NaN 的简称:
1 | array(['PR', 'USA'], dtype=object) |
只有 PR 和 USA 是没有完整的名字的。
为没有完整州名的简称补充名字
为找到的这些 state/region 的 state 项补上正确的值,从而去除掉 state 这一列的所有 NaN。
首先,我们找到所有的 PR,然后将其全名更改为 People’s Republic。这需要分两步,找到每个 PR 的索引,然后将其替换。
要找到每个 PR 的索引,只需判断内容是否为 PR,然后根据布尔值索引,拿到所有简称为 PR 的数据。最后 index 取索引即可:
1 | pr_index = abbv_pop.loc[abbv_pop['state/region'] == 'PR'].index |
这些索引为:
1 | Int64Index([2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458, |
这时,只需将找打的空值填充成对应简称的全称即可。做法也很简单,根据索引和列名,也就是 state 定位,将其赋值为 People’s Republic 就成了:
1 | abbv_pop.loc[pr_index, 'state'] = "People's Republic" |
替换后就成了:
1 | 2448 People's Republic |
同样方法,我们也可以将 USA 的全称修改为 United States:
1 | usa_index = abbv_pop.loc[abbv_pop['state/region'] == 'USA'].index |
USA 对应的 state 就被成功修改了:
1 | 2496 United States |
这时,再用 info 查看:
1 | abbv_pop.info() |
可发现,state 列已经没有空值:
1 | <class 'pandas.core.frame.DataFrame'> |
使用 isnull 结合 any 查看:
1 | abbv_pop.isnull().any(axis=0) |
得到同样的结论:
1 | state False |
合并各州面积数据 areas
我们前面一直操作的是两个表,人口和州名简称表,的内容。但是我们明明读取了三张表,还有一张州面积表没有用到。这里,我们就先把州的面积合并到人口和州名表中。
很简单,一条 merge 命令即可:
1 | abbv_pop_area = pd.merge(abbv_pop, area, on='state', how='outer') |
就拿到了合并后的数据:
1 | state state/region ages year population area (sq. mi) |
找出 area 缺失数据的行
先查看一下,新得到的总表中,那些列含有空数据:
1 | abbv_pop_area.info() |
info 数据为:
1 | <class 'pandas.core.frame.DataFrame'> |
使用 isnull 和 any 查看:
1 | abbv_pop_area.isnull().any(axis=0) |
结果为:
1 | state False |
我们会发现 area(sq.mi)
这一列有比较多的缺失数据,让我们找出是哪些行。
这实现起来很简单啦,还是老一套,判断是否为空,根据布尔值索引,拿到数据:
1 | null_area = abbv_pop_area.loc[abbv_pop_area['area (sq. mi)'].isnull()] |
成功拿到 area 为空的那些行:
1 | state state/region ages year population area (sq. mi) |
使用 index 即可拿到这些行的索引:
1 | null_area_index = null_area.index |
area 为空的行为:
1 | Int64Index([2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458, |
去除面积数据缺失的行
面积缺失的行索引已经找到,把它们去除十分容易,只需要 drop 即可:
1 | abbv_pop_area.drop(null_area_index, axis=0, inplace=True) |
从 info 中可以看出,area 列已经没有空数据了:
1 | <class 'pandas.core.frame.DataFrame'> |
找出 2010 年的全民人口数据
2010 年的全民人口,也就是年份为 2010,年龄段为 total 的数据。通过判断之后用布尔值索引是可以实现的。不过这里,我们还可以采用条件查询的方式,使用 query 参数进行多条件查询:
1 | abbv_pop_area.query('year == 2010 & ages == "total"') |
成功获取到 2010 年各州的全民人口数据:
1 | state state/region ages year population area (sq. mi) |
计算各州的人口密度
人口密度可以用公式 总人口/面积
来计算。
1 | abbv_pop_area['population'] / abbv_pop_area['area (sq. mi)'] |
我们就得到了人口密度的数据:
1 | 0 21.316769 |
不过我们并不希望仅仅得到这么单独一列数据。我们想要把人口密度作为一个新列,添加到汇总表中,这样更便于查看。这时,只需对汇总表指定列名,将新列数据放进去即可:
1 | abbv_pop_area['density'] = abbv_pop_area['population'] / abbv_pop_area['area (sq. mi)'] |
人口密度数据就成功被添加到汇总表中:
1 | state state/region ages year population area (sq. mi) density |
排序,并找出人口密度最高的州
是用 sort_values 方法可以对数据进行排序:
1 | abbv_pop_area.sort_values(by='density', axis=0, ascending=False) |
参数解释:
- by,用来指定以那一行/列进行排序
- axis,用来指定排序的方向,0 为纵向排序,1 为横向排序
- ascending,用来指定是否升序,True 为升序,False 为降序
我们就顺利拿到排序好的结果:
1 | state state/region ages year population area (sq. mi) density |
因为是按照人口密度降序排列,所以最上面的一条数据,就是对应着人口密度最高的州:
1 | abbv_pop_area.sort_values(by='density', axis=0, ascending=False).iloc[0]['state'] |
结果为:
1 | 'District of Columbia' |
符合预期。
至此,我们就完成了简单的人口数据分析。