这是github上的一个项目,作为数据分析基础入门是非常好的,于是我把它译为中文,希望对大家有所帮助。
代码均已在本地测试通过。
要求
在此存储库中,我将记录收集数据,处理数据并进行可视化的完整过程。
该项目中使用的数据集是国家数据-社会保障卡应用中心的婴儿名字,其中包括1880年至2018年的记录。
需要安装python3与以下库:
- Requests - 下载数据集.
- pandas - 进行数据分析.
- NumPy - 矩阵运算.
- Matplotlib - 绘图.
- seaborn - 美化图.
获取数据
import requests
url = "https://www.ssa.gov/oact/babynames/names.zip"
with requests.get(url) as response:
with open("names.zip", "wb") as temp_file:
temp_file.write(response.content)
提示:如果因为网速等问题下载失败,可手动复制链接到浏览器中下载。
这就将数据的zip包下载到我们电脑中了,它包括了各年份数据的txt文件与一个说明pdf文档,我们需要做的是读取txt文件,进行处理并保存到csv中。这个新的csv文件将成为我们的数据集,并将包含4个字段(年份,名称,性别和数量)
import csv
from zipfile import ZipFile
# 这两个都是内置模块,无需安装
data_list = [["year", "name", "gender", "count"]]
# 此列表将保存我们的所有数据。我们使用标题行对其进行初始化。
with ZipFile("names.zip") as temp_zip: # 读取zip对象
for file_name in temp_zip.namelist(): # 读取文件列表
if ".txt" in file_name: # 只读取txt文档
with temp_zip.open(file_name) as temp_file: # 从压缩包中读取这个txt
for line in temp_file.read().decode("utf-8").splitlines():
# 文件以二进制形式打开,我们使用utf-8对其进行解码,以便可以将其作为字符串进行操作。
# 准备好我们所需的数据字段并将其添加到列表中.
line_chunks = line.split(",")
year = file_name[3:7]
name = line_chunks[0]
gender = line_chunks[1]
count = line_chunks[2]
data_list.append([year, name, gender, count])
# 保存成csv
csv.writer(open("data.csv", "w", newline="",
encoding="utf-8")).writerows(data_list)
首先我们声明一个data_list
,它是一个二维数组。目的是为了稍后我们可以用csv.writer.writerows()
方法保存到csv中。(我更喜欢用writerows()
而不是writerow()
,因为它更快)
接下来的步骤都有注释,就不再赘述了。
数据探索(Data Exploration)
基础信息
首先导入相关库
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
接着我们使用pandas.read_csv()
方法读取csv
df = pd.read_csv("data.csv")
# 由于我们的数据集非常简单,因此我们不需要设置任何特殊标志或额外参数,默认值就足够了。
让我们从经典的df.head()
和df.tail()
方法开始,他们分别能查看数据集前几行和后几行。
df.head() # 前五行
>>> year name gender count
0 1880 Mary F 7065
1 1880 Anna F 2604
2 1880 Emma F 2003
3 1880 Elizabeth F 1939
4 1880 Minnie F 1746
df.tail() # 后五行
>>> year name gender count
1957041 2018 Zylas M 5
1957042 2018 Zyran M 5
1957043 2018 Zyrie M 5
1957044 2018 Zyron M 5
1957045 2018 Zzyzx M 5
这些样本告诉我们以下信息:
- 有4列(年份,名称,性别和计数)。
- 有1,957,046行。
- 行按年份排序。
- 女性记录显示在男性记录之前。
- 2018年,至少有5个父母将其儿子命名为``Zzyzx''。
唯一名称
df["name"].nunique() # 拥有独一无二名字的人有多少
df[df["gender"] == "M"]["name"].nunique() # 女性拥有独一无二名字的人有多少
df[df["gender"] == "F"]["name"].nunique() # 男性拥有独一无二名字的人有多少
# 性别中立的人有多少拥有独一无二的名字
both_df = df.pivot_table(index="name", columns="gender", values="count", aggfunc=np.sum).dropna()
both_df.index.nunique()
可以看出,数据集中有98,400个人拥有唯一名称。其中,男性41,475名,女性67,698名,性别中立10,773名。
十大常用名字
为了获得前10个最常用的男性和女性名字,我们将首先用dataframe
按性别过滤。
有了特定性别dataframe
后,我们将只选择2个字段,即姓名和人数。然后在name字段上使用groupby()
方法,并使用sum()
汇总结果。
最后,我们将对count字段上的值进行降序排序,并使用head(10)
方法获得前10个结果。
df = df[df["gender"] == "M"]
df = df[["name", "count"]]
df = df.groupby("name")
df = df.sum()
df = df.sort_values("count", ascending=False)
df.head(10)
>>>
name count
James 5164280
John 5124817
Robert 4820129
Michael 4362731
William 4117369
David 3621322
Joseph 2613304
Richard 2565301
Charles 2392779
Thomas 2311849
上面的代码可以写到一起,即:
df[df["gender"] == "M"][["name", "count"]].groupby("name").sum().sort_values("count", ascending=False).head(10)
当然,把"gender"改成"F"就可以找女性十大常用名了。
前二十大性别中立者常用名
这有点挑战,首先我们需要旋转dataframe
,以便名称成为索引,性别成为列,所有计数的总和(每个名称,每个性别)将成为我们的值。
我们将分步骤进行。首先,我们旋转表并删除值为0的行。即名称不在男性或女性中的行。
df = df.pivot_table(index="name", columns="gender", values="count", aggfunc=np.sum).dropna()
会输出类似的数据:
gender F M
name
Aaden 5.0 4828.0
Aadi 16.0 851.0
Aadyn 16.0 516.0
Aalijah 149.0 212.0
Aaliyah 87442.0 96.0
... ...
有了这种形状的数据,我们现在知道每个姓名每个性别有多少条记录。
现在,我们仅考虑那些至少具有50,000个性别记录的名称。
df = df[(df["M"] >= 50000) & (df["F"] >= 50000)]
df.head(20)
注意:原本我只使用前10个名字,但由于只有20个符合我们标准的名字,我将其推到了20个。
姓名记录最高和最低的年份
现在,我们将知道哪个年份的记录之和最高和最低。
both_df = df.groupby("year").sum()
male_df = df[df["gender"] == "M"].groupby("year").sum()
female_df = df[df["gender"] == "F"].groupby("year").sum()
# 组合最小值(年份和计数)
both_df.min()["count"]
both_df.idxmin()["count"]
# 男性最小值(年份和计数)
male_df.min()["count"]
male_df.idxmin()["count"]
# Female Min (count and year)
female_df.min()["count"]
female_df.idxmin()["count"]
# Combined Max (count and year)
both_df.max()["count"]
both_df.idxmax()["count"]
# Male Max (count and year)
male_df.max()["count"]
male_df.idxmax()["count"]
# Female Max (count and year)
female_df.max()["count"]
female_df.idxmax()["count"]
绘图
在此项目中,我们仅使用线绘图,这对于显示值随时间的变化非常有帮助。
首先要做的是设置一些自定义颜色,这些颜色将全局应用于每个图。
# 这些参数生成带有淡紫色的图。
sns.set(style="ticks",
rc={
"figure.figsize": [12, 7],
"text.color": "white",
"axes.labelcolor": "white",
"axes.edgecolor": "white",
"xtick.color": "white",
"ytick.color": "white",
"axes.facecolor": "#443941",
"figure.facecolor": "#443941"}
)
声明样式后,我们就可以绘制数据了。为了效率,接下来我会使用单线,但是不用担心,我会解释每条线的作用。
按年份计数
我们第一个线绘图将展示历年姓名记录数据的变化。
首先,为男性、女性、组合创建新的dataframes
both_df = df.groupby("year").sum()
male_df = df[df["gender"] == "M"].groupby("year").sum()
female_df = df[df["gender"] == "F"].groupby("year").sum()
我们直接绘制dataframes。索引为x轴,总数为y轴。
plt.plot(both_df, label="Both", color="yellow")
plt.plot(male_df, label="Male", color="lightblue")
plt.plot(female_df, label="Female", color="pink")
设置y轴的间距为500,000,首先我们格式化标签的数据,然后使用实际数据作为间隔。
yticks_labels = ["{:,}".format(i) for i in range(0, 4500000+1, 500000)]
plt.yticks(np.arange(0, 4500000+1, 500000), yticks_labels)
添加一些其他设置项
plt.legend()
plt.grid(False)
plt.xlabel("Year")
plt.ylabel("Records Count")
plt.title("Records per Year")
plt.show()
最受欢迎名字的增长情况
首先,我们合并男性和女性的值,然后旋转表,让名称是索引,年份是列。我们还用零填充缺失值。
pivoted_df = df.pivot_table(
index="name", columns="year", values="count", aggfunc=np.sum).fillna(0)
然后按年份计算每个名字的百分比。
percentage_df = pivoted_df / pivoted_df.sum() * 100
增加新的一列作为百分比之和
percentage_df["total"] = percentage_df.sum(axis=1)
我们对dataframe进行排序以检查哪些是最高值并将进行切片。之后,我们删除total列,因为将不再使用它。
sorted_df = percentage_df.sort_values(
by="total", ascending=False).drop("total", axis=1)[0:10]
翻转轴,以便更轻易地绘图
transposed_df = sorted_df.transpose()
我们通过使用列名作为标签和Y轴来单独地绘制每个名字。
for name in transposed_df.columns.tolist():
plt.plot(transposed_df.index, transposed_df[name], label=name)
设置y轴间距为0.5%
yticks_labels = ["{}%".format(i) for i in np.arange(0, 5.5, 0.5)]
plt.yticks(np.arange(0, 5.5, 0.5), yticks_labels)
增加最后的自定义项
plt.legend()
plt.grid(False)
plt.xlabel("Year")
plt.ylabel("Percentage by Year")
plt.title("Top 10 Names Growth")
plt.show()
十大热门名字
我们将获得过去10年(2008-2018)的最热门的十个名字变化趋势
和上面的思路类似,直接上代码
# 首先要做的是删除所有早于2008年的记录。
filtered_df = df[df["year"] >= 2008]
# 然后,我们合并男性女性值,并对表进行数据透视,因此名称是我们的索引,年份是我们的列。我们还用零填充缺失值。
pivoted_df = filtered_df.pivot_table(
index="name", columns="year", values="count", aggfunc=np.sum).fillna(0)
# 然后我们按年份计算每个名称的百分比。
percentage_df = pivoted_df / pivoted_df.sum() * 100
# 我们添加了一个新列来表示百分比之和。
percentage_df["total"] = percentage_df.sum(axis=1)
# 我们对dataframe进行排序以检查哪些是最高值并将进行切片。之后,我们删除total列,因为将不再使用它。
sorted_df = percentage_df.sort_values(
by="total", ascending=False).drop("total", axis=1)[0:10]
# 翻转轴,以便更轻松地绘制数据框。
transposed_df = sorted_df.transpose()
# 我们通过使用列名作为标签和Y轴来单独地绘制每个名字。
for name in transposed_df.columns.tolist():
plt.plot(transposed_df.index, transposed_df[name], label=name)
# Y轴0.5%的间距
yticks_labels = ["{:.2f}%".format(i) for i in np.arange(0.3, 0.7, 0.05)]
plt.yticks(np.arange(0.3, 0.7, 0.05), yticks_labels)
# 从2008年到2018年,我们将x轴的间距设为1
xticks_labels = ["{}".format(i) for i in range(2008, 2618+1, 1)]
plt.xticks(np.arange(2008, 2018+1, 1), xticks_labels)
# 最后设置
plt.legend()
plt.grid(False)
plt.xlabel("Year")
plt.ylabel("Percentage by Year")
plt.title("Top 10 Trending Names")
plt.show()
版权属于:作者名称
本文链接:https://www.sitstars.com/archives/43/
转载时须注明出处及本声明