愿所有人都不被癌症折磨
疾病,尤其是癌症的防治是关乎全世界人民幸福生活的一个巨大主题,本实验通过对美国联邦相关统计部门发布的1999年以来美国人民患癌症情况的调查数据的分析,研究及可视化了美国癌症与年龄、性别等因素的关系。
输入并执行魔法命令 %matplotlib inline, 去除图例、坐标轴右侧、坐标轴顶部边框。
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['legend.frameon'] = False
plt.rcParams['axes.spines.right'] = False
plt.rcParams['axes.spines.top'] = False
准备数据
数据集介绍:
本数据集来自美国疾病防治中心(CDC),美国癌症统计( United States Cancer Statistics,USCS)是自 1999 年,由联邦相关统计部门发起的对全美 51 个州进行的癌症人口数量调查、登记活动,数据真实、可性度高。
导入数据并查看前 5 行。
import pandas as pd
df = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/3023/American_USCS_cancers.txt', sep='\t')
df.head()
从运行结果可知,本数据集主要由癌症种类(Cancer Sites)、性别(Sex)、年龄组(Age Groups)、癌症数量(Count)四个特征构成。
以下对数据进行清洗和处理,主要包括:
对没有癌症数量的行进行删除;
年龄组为离散变量,将其转化成连续变量更有利于分析(此处用正则的方式提取数字后进行均值的计算);
删除癌症种类中处于合并概念的特征,例如 Male and Female Breast 是男性和女性乳腺癌的总和,但癌症特征列分别有男性和女性乳腺癌,因此该特征是对次级特征的重复计数。
import re
import numpy as np
# 挑选出Count列不为空的行
df = df.loc[~pd.isna(df['Count'])]
# 选出需要的特征
df = df[['Cancer Sites', 'Sex', 'Age Groups Code', 'Count']]
# 用正则提取年龄组数据,并进行平均,<1的年龄组平均值取1,>85的年龄组,年龄平均值取85
def get_avg(x):
min_val, max_val = re.findall('(\d+)[+-]?(\d+)?', x)[0]
if max_val.isdigit():
pass
else:
max_val = min_val
return np.mean([float(min_val), float(max_val)])
df['Age Groups Code'] = df['Age Groups Code'].apply(lambda x: get_avg(x))
# 删除以下父级癌症类别
leading_cancer_sites = ['Digestive System',
'Male Genital System',
'Urinary System',
'All Invasive Cancer Sites Combined',
'Male and Female Breast',
'Male and Female Breast, In Situ',
'Male Genital System'
'Colon and Rectum',
'Lymphomas',
'Non-Hodgkin Lymphoma',
'Hodgkin Lymphoma',
'Colon and Rectum',
'Female Genital System',
'Brain and Other Nervous System',
'Endocrine System',
'Leukemias',
'Skin excluding Basal and Squamous',
'Oral Cavity and Pharynx',
'Respiratory System']
for cancer_site in leading_cancer_sites:
df = df.loc[df['Cancer Sites'] != cancer_site]
df.head()
男性中排名前 10 的癌症类别
对癌症类别按癌症人数进行聚合获得前10的癌症种类,通过 plt.stackplot 接口对其进行堆积面积图的绘制。需要注意的是,与大多数接口不同,plt.stackplot 接口需要一次性传入需要堆积的数据,即,其传入的是需要被可视化序列的列表,因此绘图时传入的数据参数为 data.T 即矩阵数据的转置。
data = df.loc[df['Sex'] == 'Male']
# 男性癌症总数前10项
male_10 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:10]
# 以年龄为行,癌症种类为列,癌症数量为值对数据进行透视,值的计算方程为求和
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='mean',
columns='Cancer Sites',)
# 选取排名前10的癌症类别
data = data[male_10]
data.head()
运行以下代码完成可视化。
fontsize = 17
plt.rcParams['xtick.labelsize'] = fontsize
plt.rcParams['ytick.labelsize'] = fontsize
plt.rcParams['axes.labelsize'] = fontsize
plt.rcParams['axes.titlesize'] = fontsize
plt.rcParams['legend.fontsize'] = fontsize
plt.rcParams['figure.figsize'] = (15, 6)
plt.stackplot(data.index, data.T, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population')
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 Cancers of American Men Varies with Age')
从输出结果可以看出:
大多数的男性癌症从40随开始出现,在 65-75 年龄组人数最多;
男性癌症中,人数最多的是前列腺相关的癌症。
虽然本课程主要是介绍可视化的,但是为了大家的身体健康和癌症预防,我们还是尽量把大家关注的问题细化,以下是前 10 癌症类别的英文对照翻译:
坐标数值格式化
对于诸如人口一类的数据,由于其数据量纲较大,因此在可视化过程中,其 y 轴显示的数据也较大,会带来一定程度地阅读困难(数字 0 太多,不好数),可以通过以下几种方式解决:
方式一:减小数据量纲并通过 y 轴标题进行单位解释,本例将数据除以 100 万,并在 y 轴轴标题后增加了单位说明。
plt.rcParams['figure.figsize'] = (15, 6)
plt.stackplot(data.index, data.T/1000000, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population /million')
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 Cancers of American Men Varies with Age')
方式二:通过格式化文本 FormatStrFormatter 类
from matplotlib.ticker import FormatStrFormatter
plt.rcParams['figure.figsize'] = (15, 6)
plt.stackplot(data.index, data.T, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population')
ax = plt.gca()
# 使用科学计数法,减少0的显示
ax.yaxis.set_major_formatter(formatter=FormatStrFormatter('%0.1e'))
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 Cancers of American Men Varies with Age')
方式三:通过格式化函数 FuncFormatter 类
from matplotlib.ticker import FuncFormatter
plt.rcParams['figure.figsize'] = (15, 6)
plt.stackplot(data.index, data.T, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population /m:million')
# 定义格式化字符的函数
def major_tick_format(num, pos):
return '%.1f m' % (num/1000000)
ax = plt.gca()
formatter = FuncFormatter(major_tick_format)
ax.yaxis.set_major_formatter(formatter=formatter)
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 Cancers of American Men Varies with Age')
女性中排名前 10 的癌症类别
筛选出数据集中的女性,进行相同的数据处理。
plt.rcParams['figure.figsize'] = (15, 6)
data = df.loc[df['Sex'] == 'Female']
female_10 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:10]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Cancer Sites',)
data = data[female_10]
plt.stackplot(data.index, data.T/1000000, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population /million')
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 Cancers of American Women Varies with Age')
从输出结果可以看出:
相比于男性,女性患癌症的年龄相对较早,从 30 岁开始癌症人数逐步升高,且年龄分布也相对较宽。
前 10 种癌症中英文对照如下:
40 岁以前年龄组中排名前15的癌症类别
从所有人群中选出平均年龄小于 40 岁的年龄组,进行相同的数据处理得到排名前 15 的癌症类别。
plt.rcParams['figure.figsize'] = (15, 6)
data = df.loc[df['Age Groups Code'] <= 40]
top_15 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:15]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Cancer Sites',)
data = data[top_15]
plt.stackplot(data.index, data.T/1000000, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population /million')
plt.legend(loc=2, ncol=2)
plt.title('The Top 15 Cancers of American Varies with Age (Less Than 40)')
运行输出结果,结果前 15 癌症类别的中英文翻译为:
男性各年龄层归一化后前 10 大癌症类别
将数据透视后的数据进行按行(按年龄)的归一化处理,可以得到前 10 癌症在各年龄层不同的患病比例。
plt.rcParams['figure.figsize'] = (9, 12)
data = df.loc[df['Sex'] == 'Male']
male_10 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:10]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Cancer Sites',)
data = data[male_10]
# 将空值填充为0 ,为空值的数据大多为5岁以下年龄组,查看数据发现该年龄很小概率会患该癌症,所有合理认为其数据为0
data.fillna(value=0, inplace=True)
# 按年龄层进行归一化处理
sums = data.sum(axis=1)
for col in data.columns:
data[col] = data[col]/sums*100
plt.stackplot(data.index, data.T, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population ratio /%')
plt.xlim(0.5, 85)
plt.ylim(0, 100)
plt.legend(bbox_to_anchor=(1.0, 0.7), loc='upper left', ncol=1)
plt.title('The Uniformization Age Cancers of American Men')
从输出结果可以看出:
在小于 20 岁的年龄组,非霍奇金淋巴瘤(NHL-Nodal)的患病概率远高于其他癌症;
40 岁以后,男性患前列腺(Prostate)相关癌症的概率开始显著升高,且在 60 岁达到顶峰;
肺癌和支气管(Lung and Bronchus)是继前列腺相关癌症后,男性的第二大癌症,其分布顶峰也在 60 岁附近;
胰腺癌(Pancreas)的发病随年龄逐渐增加。
女性各年龄层归一化后前 10 大癌症类别
女性数据集做相同处理,得到前 10 癌症在各年龄层不同的患病比例。
plt.rcParams['figure.figsize'] = (9, 12)
data = df.loc[df['Sex'] == 'Female']
female_10 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:10]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Cancer Sites',)
data = data[female_10]
sums = data.sum(axis=1)
for col in data.columns:
data[col] = data[col]/sums*100
data.fillna(value=0, inplace=True)
plt.stackplot(data.index, data.T, labels=data.columns)
plt.xlabel('Age')
plt.ylabel('Cancer population ratio /%')
plt.xlim(0.5, 85)
plt.ylim(0, 100)
plt.legend(bbox_to_anchor=(1.0, 0.7), loc='upper left', ncol=1)
plt.title('The Uniformization Age Cancers of American Women')
从输出结果可以看出:
20 岁以前,女性患癌症相对较分散,小于 10 岁时白血病相关(Miscellaneous)较多,一定数量的女性在青春期会患卵巢(Ovary)相关疾病;
甲状腺(Thyroid)相关疾病在 20 岁女性人群中达到较高比例;
乳腺癌从 20 岁开始所占比例逐步上升,在 40 岁左右比例达到峰值;
70 岁左右患肺和呼吸道相关癌症比例仅次于乳腺癌,排名第二。
排名前 10 的癌症类别相对人数对比
上面的三个实验通过堆积面积图的形式可视化数据,可以向读者直观呈现堆积效应和主效应(最大面积块所代表的数据序列为主效应)。正因为如此,细节部分的表述往往一笔带过,堆积的关系无法表达出各系列的绝对差异,plt.fill_between 接口绘制的填充图有效地解决了这个问题。
plt.rcParams['figure.figsize'] = (15, 7)
for sex in ['Male', 'Female']:
data = df.loc[df['Sex'] == sex]
male_10 = data.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:10]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Cancer Sites',)
data = data[male_10]
for col in male_10:
y = data[col]/1000
plt.fill_between(data.index, y, alpha=0.3)
plt.plot(data.index, y, lw=6, label=col,)
plt.xlabel('Age')
plt.ylabel('Cancer population /k')
plt.legend(loc=2, ncol=2)
plt.title('The Top 10 cancers in American Varies with Age for %s' % (sex))
从运行结果可以看出,男性与女性前 10 类癌症在各年龄段的绝对数量比堆积面积图更为清晰,表现在:
各类癌症随年龄的变化趋势和相对差异更为清晰。剪除了堆积效应,可以更直观地发现,男性患病年龄在 45 岁出现了一个拐点,女性在 35 岁左右;
男性的主要癌症集中在前列腺癌,其总人数峰值在 70 岁左右,女性主要是乳腺癌,主要患病年龄段跨度较大 40 - 80 岁,峰值出现在 65 岁左右;
男性和女性第二大癌症均为肺及支气管相关癌症,且患病年龄较为一致。
患癌症年龄的性别对比
从性别的角度按年龄对患癌症人数进行聚合。
plt.rcParams['figure.figsize'] = (10, 6)
data = df
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Sex',)
colors = {'Male': 'tab:blue', 'Female': 'tab:red'}
for col in data.columns:
color = colors[col]
plt.fill_between(data.index, data[col]/1000000, alpha=0.3, color=color)
plt.plot(data.index, data[col]/1000000, lw=6, label=col, color=color)
plt.xlabel('Age')
plt.ylabel('Cancer population /million')
plt.legend(loc=2, ncol=3)
plt.title('The Cancers population varies by gender and age')
从运行结果可以看出,男性患癌症的年龄分布更趋向于高龄且更为集中,而女性则分布相对均匀,人数拐点早于男性。
前20大癌症在男女性别上的分布差异
从实验「排名前 10 的癌症类别相对人数对比」可知,男女性在癌症年龄表现上有较大差异,以下进一步研究在前 20 大癌症上,性别对患癌症有怎样的影响。
画布生成 4 * 5 总计 20 个子图,每个子图可视化 1 种癌症的性别差异。
fontsize = 14
# 由于子图相对较小,将全图字号减小至 14 号
plt.rcParams['xtick.labelsize'] = fontsize
plt.rcParams['ytick.labelsize'] = fontsize
plt.rcParams['axes.labelsize'] = fontsize
plt.rcParams['axes.titlesize'] = fontsize
plt.rcParams['legend.fontsize'] = fontsize
rows, cols = 4, 5
top_ = df.groupby(['Cancer Sites'])['Count'].sum(
).sort_values(ascending=False).index[:rows*cols]
fig, axs = plt.subplots(rows, cols, sharex=True, figsize=(3.8*cols, 3.0*rows))
axs = axs.ravel()
for i, cancer in enumerate(top_):
ax = axs[i]
data = df.loc[df['Cancer Sites'] == cancer]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Sex',)
data = data/100000
# 不同性别绘制不同的颜色
colors = {'Male': 'tab:blue', 'Female': 'tab:red'}
for col in data.columns:
color = colors[col]
ax.fill_between(data.index, data[col], alpha=0.3, color=color)
ax.plot(data.index, data[col], lw=6, label=col, color=color)
ax.set_title(cancer)
# 第二张图绘制图例,原因是第一张图只有女性,图例不够完全
if i == 1:
ax.legend()
# 子图的最后一行添加 x 坐标轴标题
if i >= (rows-1)*cols:
ax.set_xlabel('Age')
# 子图的第一列添加 y 坐标轴标题
if i % cols == 0:
ax.set_ylabel('Cancer population/hk')
fig.suptitle(
'The Top %d Cancers Population Varies With Gender and Age' % (rows*cols), va='top', size=22)
# 堆积 y 轴坐标轴标题
fig.align_ylabels()
# 调节子图的宽度和高度间距
plt.subplots_adjust(wspace=0.2, hspace=0.3)
从运行结果可以看出:
显而易见地,部分疾病有性别差异,例如乳腺、卵巢相关癌症只分布在女性,前列腺相关疾病只分布在男性;
排名第二的肺及支气管相关癌症,在性别上随年龄患病人数趋势大致相同;
泌尿系统(Urinary)、肝脏(Liver)相关疾病,男性显著地高于女性;
甲状腺(Thyroid)类相关疾病,女性显著地高于男性。
小于20岁年龄组在男女性别上的分布差异
从实验「排名前 10 的癌症类别相对人数对比」可知,男女性在癌症年龄表现上有较大差异,由于小于20岁年龄组癌症人数较少,其性别差异在整体图上并不明显,以下进一步研究在前 20 大癌症上,低年龄组人群种,性别对患癌症有怎样的影响。
rows = 4
cols = 5
limit_age = 20
top_ = df.loc[df['Age Groups Code'] <= limit_age].groupby(
['Cancer Sites'])['Count'].sum().sort_values(ascending=False).index[:rows*cols]
fig, axs = plt.subplots(rows, cols, sharex=True, figsize=(3.8*cols, 3.0*rows))
axs = axs.ravel()
for i, cancer in enumerate(top_):
ax = axs[i]
data = df.loc[df['Age Groups Code'] <= limit_age]
data = data.loc[data['Cancer Sites'] == cancer]
data = data.pivot_table(values='Count',
index='Age Groups Code',
aggfunc='sum',
columns='Sex',)
data = data/10000
colors = {'Male': 'tab:blue', 'Female': 'tab:red'}
for col in data.columns:
color = colors[col]
ax.fill_between(data.index, data[col], alpha=0.3, color=color)
ax.plot(data.index, data[col], lw=6, label=col, color=color)
ax.set_title(cancer)
if i == 0:
ax.legend()
if i >= (rows-1)*cols:
ax.set_xlabel('Age')
if i % cols == 0:
ax.set_ylabel('Cancer population /W')
fig.suptitle(
'The Top %d Cancers Population Varies With Gender and Age(Less Than %d)' % (rows*cols, limit_age), va='top', size=22)
fig.align_ylabels()
plt.subplots_adjust(wspace=0.2, hspace=0.3)
从运行结果可以看出:
低年龄组中,除生殖系统(Testis、Ovary)相关疾病以外,在大多数癌症上并未体现出性别差异;
男性在以 NHL-Nodal 和 NHL-Extranodal 为代表的非霍奇金淋巴瘤(Non-Hodgkin Lymphoma,强两者为其子类)上显著高于女性。
可视化分层年龄组的绝对差异
上面的实验可视化了任意年龄(连续型数据)的性别差异,而对于组间差异,面积或填充图无法可视化。原因是:
虽然数据可视化了连续区间任意位置的数据,但是没有办法获取任意区间(采样层)的聚合特征(常用均值、中位数等表达)和离散特征(常用标准偏差等表达);
任意点的高低绝对水平并不能代表整体高低水平,无法进行显著性差异对比;
常用于进行分层差异比较的绘图对象为哑铃图、直方图、箱线图、误差线图等。
哑铃图绘制原理
哑铃图有一根带有两端标记的直线构成,因其形状类似“哑铃”而得名,其绘制原理是通过 plt.scatter 散点图接口绘制端点的标记,通过辅助线 plt.vlines 绘制中间的直线。 哑铃图上部点表示的数据为该采样层 均值 + 标准偏差,下部点表示 均值 - 标准偏差。 两根哑铃图之间绘制填充面,填充面颜色根据两端聚合结果的绝对值大小分别赋值不同的颜色。 其过程为:
准备数据
data = df
# 获得所有癌症,性别的均值和标准偏差
data = data.groupby(['Cancer Sites', 'Sex'], as_index=False)[
'Count'].agg(['mean', 'std'])
data['Cancer Sites'] = data.index.droplevel(level=1)
data['Sex'] = data.index.droplevel(level=0)
# 查看数据前 5 行
data.head()
获取某一个癌症类在不同性别人群的均值和标准偏差数据。
tmp = data.loc[data['Cancer Sites'] == 'Acute Lymphocytic Leukemia']
f_mean, f_std, m_mean, m_std = np.array(tmp[['mean', 'std']]).ravel()
f_mean, f_std, m_mean, m_std
绘制哑铃图。
s = 80
plt.scatter(0, f_mean+f_std, color='tab:red', s=s)
plt.scatter(0, f_mean-f_std, color='tab:red', s=s)
plt.scatter(1, m_mean+m_std, color='tab:blue', s=s)
plt.scatter(1, m_mean-m_std, color='tab:blue', s=s)
plt.vlines(0, ymin=f_mean-f_std, ymax=f_mean+f_std, lw=4, color='tab:red')
plt.vlines(1, ymin=m_mean-m_std, ymax=m_mean+m_std, lw=4, color='tab:blue')
绘制填充面。
plt.scatter(0, f_mean+f_std, color='tab:red', s=s)
plt.scatter(0, f_mean-f_std, color='tab:red', s=s)
plt.scatter(1, m_mean+m_std, color='tab:blue', s=s)
plt.scatter(1, m_mean-m_std, color='tab:blue', s=s)
plt.vlines(0, ymin=f_mean-f_std, ymax=f_mean+f_std, lw=4, color='tab:red')
plt.vlines(1, ymin=m_mean-m_std, ymax=m_mean+m_std, lw=4, color='tab:blue')
# 绘制填充面
# 将哑铃图上部、下面两个点进行线性拟合
f_top = np.polyfit(x=[0, 1], y=[f_mean+f_std, m_mean+m_std], deg=1)
f_bottom = np.polyfit(x=[0, 1], y=[f_mean-f_std, m_mean-m_std], deg=1)
# 生成填充面的 x,y 坐标
xs = np.linspace(0, 1, 100)
y_tops = np.polyval(f_top, xs)
y_bottoms = np.polyval(f_bottom, xs)
# 根据均值,确定填充面填充颜色
if np.sum(f_mean > m_mean):
plt.fill_between(xs, y_tops, y_bottoms, color='tab:red', alpha=0.6)
else:
plt.fill_between(xs, y_tops, y_bottoms, color='tab:blue', alpha=0.6)
年龄组 < 20 岁
根据以上过程,获得年龄组在 20 岁以下人群在前 20 项共有癌症上的组间差异。
fontsize = 16
plt.rcParams['xtick.labelsize'] = fontsize
plt.rcParams['ytick.labelsize'] = fontsize
plt.rcParams['axes.labelsize'] = fontsize
plt.rcParams['axes.titlesize'] = fontsize
plt.rcParams['legend.fontsize'] = fontsize
rows, cols = 4, 5
limit_age = 20
top_df = df.loc[df['Age Groups Code'] <= limit_age].pivot_table(
values='Count', index='Cancer Sites', columns='Sex',)
top_df['sum'] = top_df.sum(axis=1)
top_df.dropna(inplace=True)
top_df = top_df.sort_values(['sum'], ascending=False)
top_ = top_df.index[:rows*cols]
data = df.loc[df['Age Groups Code'] <= limit_age]
data = data.groupby(['Cancer Sites', 'Sex'], as_index=False)[
'Count'].agg(['mean', 'std'])
data = data/1000
data['Cancer Sites'] = data.index.droplevel(level=1)
data['Sex'] = data.index.droplevel(level=0)
fig, axs = plt.subplots(rows, cols, sharex=True, figsize=(3.8*cols, 3.0*rows))
axs = axs.ravel()
for i, cancer in enumerate(top_):
ax = axs[i]
tmp = data.loc[data['Cancer Sites'] == cancer]
f_mean, f_std, m_mean, m_std = np.array(tmp[['mean', 'std']]).ravel()
# 哑铃图
s = 80
ax.scatter(0, f_mean+f_std, color='tab:red', s=s)
ax.scatter(0, f_mean-f_std, color='tab:red', s=s)
ax.scatter(1, m_mean+m_std, color='tab:blue', s=s)
ax.scatter(1, m_mean-m_std, color='tab:blue', s=s)
ax.vlines(0, ymin=f_mean-f_std, ymax=f_mean+f_std, lw=4, color='tab:red')
ax.vlines(1, ymin=m_mean-m_std, ymax=m_mean+m_std, lw=4, color='tab:blue')
ax.set_xticks([0, 1])
ax.set_xticklabels(['Female', 'Male'])
ax.set_xlim(-0.3, 1.2)
# 面积图
f_top = np.polyfit(x=[0, 1], y=[f_mean+f_std, m_mean+m_std], deg=1)
f_bottom = np.polyfit(x=[0, 1], y=[f_mean-f_std, m_mean-m_std], deg=1)
xs = np.linspace(0, 1, 100)
y_tops = np.polyval(f_top, xs)
y_bottoms = np.polyval(f_bottom, xs)
if np.sum(f_mean > m_mean):
ax.fill_between(xs, y_tops, y_bottoms, color='tab:red', alpha=0.6)
else:
ax.fill_between(xs, y_tops, y_bottoms, color='tab:blue', alpha=0.6)
ax.set_title(cancer)
if i >= (rows-1)*cols:
ax.set_xlabel('Gender')
if i % cols == 0:
ax.set_ylabel('Cancer population/k', size=14)
fig.suptitle(
'The Top %d Cancers Population Varies With Gender and Age(Less Than %d)' % (rows*cols, limit_age), va='top', size=22)
fig.align_ylabels()
plt.subplots_adjust(wspace=0.2, hspace=0.3)
从输出结果可以看出:
对于排名前 20 的癌症,在本年龄组,男性患病人数均值大多数高于女性均值(蓝色填充面的子图多于红色填充面);
对于大多数癌症,性别差异并不明显,表现出四边形比较接近于矩形;
甲状腺类癌症,女性要显著性地高于男性。
年龄组 > 50 岁
同理可研究年龄组大于 50 岁人群,男性在肝脏(Liver)、直肠(Rectum)等方面的癌症显著性地高于女性。
rows, cols = 4, 5
limit_age = 50
# 高年龄段由于某些癌症只有一个性别有,因此此处将其进行了筛选
top_df = df.loc[df['Age Groups Code'] >= limit_age].pivot_table(
values='Count', index='Cancer Sites', columns='Sex',)
top_df['sum'] = top_df.sum(axis=1)
top_df.dropna(inplace=True)
top_df = top_df.sort_values(['sum'], ascending=False)
top_ = top_df.index[:rows*cols]
data = df.loc[df['Age Groups Code'] >= limit_age]
data = data.groupby(['Cancer Sites', 'Sex'], as_index=False)[
'Count'].agg(['mean', 'std'])
data = data/1000
data['Cancer Sites'] = data.index.droplevel(level=1)
data['Sex'] = data.index.droplevel(level=0)
fig, axs = plt.subplots(rows, cols, sharex=True, figsize=(3.8*cols, 3.0*rows))
axs = axs.ravel()
for i, cancer in enumerate(top_):
ax = axs[i]
tmp = data.loc[data['Cancer Sites'] == cancer]
f_mean, f_std, m_mean, m_std = np.array(tmp[['mean', 'std']]).ravel()
# 哑铃图
s = 80
ax.scatter(0, f_mean+f_std, color='tab:red', s=s)
ax.scatter(0, f_mean-f_std, color='tab:red', s=s)
ax.scatter(1, m_mean+m_std, color='tab:blue', s=s)
ax.scatter(1, m_mean-m_std, color='tab:blue', s=s)
ax.vlines(0, ymin=f_mean-f_std, ymax=f_mean+f_std, lw=4, color='tab:red')
ax.vlines(1, ymin=m_mean-m_std, ymax=m_mean+m_std, lw=4, color='tab:blue')
ax.set_xticks([0, 1])
ax.set_xticklabels(['Female', 'Male'])
ax.set_xlim(-0.3, 1.2)
# 面积图
f_top = np.polyfit(x=[0, 1], y=[f_mean+f_std, m_mean+m_std], deg=1)
f_bottom = np.polyfit(x=[0, 1], y=[f_mean-f_std, m_mean-m_std], deg=1)
xs = np.linspace(0, 1, 100)
y_tops = np.polyval(f_top, xs)
y_bottoms = np.polyval(f_bottom, xs)
if np.sum(f_mean > m_mean):
ax.fill_between(xs, y_tops, y_bottoms, color='tab:red', alpha=0.6)
else:
ax.fill_between(xs, y_tops, y_bottoms, color='tab:blue', alpha=0.6)
ax.set_title(cancer)
if i >= (rows-1)*cols:
ax.set_xlabel('Gender')
if i % cols == 0:
ax.set_ylabel('Cancer population/k', size=14)
fig.suptitle(
'The Top %d Cancers Population Varies With Gender(Age More Than %d)' % (rows*cols, limit_age), va='top', size=22)
fig.align_ylabels()
plt.subplots_adjust(wspace=0.2, hspace=0.3)
年龄组 20 - 40 岁之间
为增加代码可读性,可单独创建哑铃图的绘图函数。
def dumbbell(f_mean, f_std, m_mean, m_std, ax):
# 哑铃图
s = 80
ax.scatter(0, f_mean+f_std, color='tab:red', s=s)
ax.scatter(0, f_mean-f_std, color='tab:red', s=s)
ax.scatter(1, m_mean+m_std, color='tab:blue', s=s)
ax.scatter(1, m_mean-m_std, color='tab:blue', s=s)
ax.vlines(0, ymin=f_mean-f_std, ymax=f_mean+f_std, lw=4, color='tab:red')
ax.vlines(1, ymin=m_mean-m_std, ymax=m_mean+m_std, lw=4, color='tab:blue')
ax.set_xticks([0, 1])
ax.set_xticklabels(['Female', 'Male'])
ax.set_xlim(-0.3, 1.2)
# 面积图
f_top = np.polyfit(x=[0, 1], y=[f_mean+f_std, m_mean+m_std], deg=1)
f_bottom = np.polyfit(x=[0, 1], y=[f_mean-f_std, m_mean-m_std], deg=1)
xs = np.linspace(0, 1, 100)
y_tops = np.polyval(f_top, xs)
y_bottoms = np.polyval(f_bottom, xs)
if np.sum(f_mean > m_mean):
ax.fill_between(xs, y_tops, y_bottoms, color='tab:red', alpha=0.6)
else:
ax.fill_between(xs, y_tops, y_bottoms, color='tab:blue', alpha=0.6)
return ax
运行以下代码,获得年龄组 20 - 40 区间的组间差异图。
rows, cols = 4, 5
min_age, max_age = 20, 50
# 高年龄段由于某些癌症只有一个性别有,因此此处将其进行了筛选
top_df = df.loc[(df['Age Groups Code'] >= min_age) & (df['Age Groups Code'] <=
max_age)].pivot_table(values='Count', index='Cancer Sites', columns='Sex',)
top_df['sum'] = top_df.sum(axis=1)
top_df.dropna(inplace=True)
top_df = top_df.sort_values(['sum'], ascending=False)
top_ = top_df.index[:rows*cols]
data = df.loc[(df['Age Groups Code'] >= min_age) &
(df['Age Groups Code'] <= max_age)]
data = data.groupby(['Cancer Sites', 'Sex'], as_index=False)[
'Count'].agg(['mean', 'std'])
data = data/1000
data['Cancer Sites'] = data.index.droplevel(level=1)
data['Sex'] = data.index.droplevel(level=0)
fig, axs = plt.subplots(rows, cols, sharex=True, figsize=(3.8*cols, 3.0*rows))
axs = axs.ravel()
for i, cancer in enumerate(top_):
ax = axs[i]
tmp = data.loc[data['Cancer Sites'] == cancer]
f_mean, f_std, m_mean, m_std = np.array(tmp[['mean', 'std']]).ravel()
ax = dumbbell(f_mean, f_std, m_mean, m_std, ax)
ax.set_title(cancer)
if i >= (rows-1)*cols:
ax.set_xlabel('Gender')
if i % cols == 0:
ax.set_ylabel('Cancer population/k', size=14)
fig.suptitle(
'The Top %d Cancers Population Varies With Gender(Age Between %d And %d)' % (rows*cols, min_age, max_age), va='top', size=22)
fig.align_ylabels()
plt.subplots_adjust(wspace=0.2, hspace=0.3)