UCB Data100:数据科学的原理和技巧:第六章到第十章(3)https://developer.aliyun.com/article/1427170
8.0.1.5 深入了解displot
正如我们之前看到的,我们可以使用seaborn
的displot
函数来绘制各种分布。特别是,displot
允许您指定绘图的kind
,并且是histplot
、kdeplot
和ecdfplot
的包装器。
下面,我们可以看到一些示例,说明了如何使用sns.displot
来绘制各种分布。
首先,我们可以通过将kind
设置为"hist"
来绘制直方图。请注意,这里我们指定了stat = density
,以使直方图归一化,使得直方图下面积等于 1。
sns.displot(data=wb, x="gni", kind="hist", stat="density") # default: stat=count and density integrates to 1 plt.title("Distribution of gross national income per capita");
/Users/Ishani/micromamba/lib/python3.9/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight
现在,如果我们想生成一个 KDE 图呢?我们可以将kind
设置为"kde"
!
sns.displot(data=wb, x="gni", kind='kde') plt.title("Distribution of gross national income per capita");
/Users/Ishani/micromamba/lib/python3.9/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight
最后,如果我们想生成一个经验累积分布函数(ECDF),我们可以指定kind = "ecdf"
。
sns.displot(data=wb, x="gni", kind='ecdf') plt.title("Cumulative Distribution of gross national income per capita");
/Users/Ishani/micromamba/lib/python3.9/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight
8.1 定量变量之间的关系
到目前为止,我们已经讨论了如何可视化单变量分布。除此之外,我们还想了解数值变量对之间的关系。
8.1.0.1 散点图
散点图是表示两个定量变量之间关系的最有用的工具之一。它们在评估变量之间的关系强度或相关性方面特别重要。对这些关系的了解可以激发我们建模过程中的决策。
在matplotlib
中,我们使用函数plt.scatter
来生成散点图。请注意,与我们绘制单变量分布的示例不同,现在我们指定要沿 x 轴和 y 轴绘制的值序列。
plt.scatter(wb["per capita: % growth: 2016"], \ wb['Adult literacy rate: Female: % ages 15 and older: 2005-14']) plt.xlabel("% growth per capita") plt.ylabel("Female adult literacy rate") plt.title("Female adult literacy against % growth");
在seaborn
中,我们调用函数sns.scatterplot
。我们使用x
和y
参数来指示要沿 x 轴和 y 轴绘制的值。通过使用hue
参数,我们可以指定用于给每个散点着色的第三个变量。
sns.scatterplot(data = wb, x = "per capita: % growth: 2016", \ y = "Adult literacy rate: Female: % ages 15 and older: 2005-14", hue = "Continent") plt.title("Female adult literacy against % growth");
尽管上面的图表传达了两个变量之间的一般关系,但它们都存在一个主要限制——过度绘制。当具有相似值的散点堆叠在一起时,就会发生过度绘制,这使得很难看出实际绘制的散点数量。请注意,在图表的右上方区域,我们无法轻易地判断出有多少点已经被绘制。这使得我们的可视化难以解释。
我们有一些方法可以帮助减少过度绘制:
- 减小散点标记的大小可以提高可读性。我们可以通过将
plt.scatter
或sns.scatterplot
的大小参数s
设置为新值来实现这一点。 - 抖动是向所有 x 和 y 值添加少量随机噪声的过程,以略微移动每个数据点的位置。通过随机移动所有数据一小段距离,我们可以更清楚地区分各个点,而不会改变原始数据集的主要趋势。
在下面的单元格中,我们首先使用np.random.uniform
对数据进行抖动,然后使用较小的标记重新绘制。结果图更容易解释。
# Setting a seed ensures that we produce the same plot each time # This means that the course notes will not change each time you access them np.random.seed(150) # This call to np.random.uniform generates random numbers between -1 and 1 # We add these random numbers to the original x data to jitter it slightly x_noise = np.random.uniform(-1, 1, len(wb)) jittered_x = wb["per capita: % growth: 2016"] + x_noise # Repeat for y data y_noise = np.random.uniform(-5, 5, len(wb)) jittered_y = wb["Adult literacy rate: Female: % ages 15 and older: 2005-14"] + y_noise # Setting the size parameter `s` changes the size of each point plt.scatter(jittered_x, jittered_y, s=15) plt.xlabel("% growth per capita (jittered)") plt.ylabel("Female adult literacy rate (jittered)") plt.title("Female adult literacy against % growth");
8.1.0.2 lmplot
和jointplot
seaborn
还包括几个内置函数,用于创建更复杂的散点图。其中最常用的两个例子是sns.lmplot
和sns.jointplot
。
sns.lmplot
在一个函数调用中绘制了散点图和线性回归线。我们将在几节课中讨论线性回归。
sns.lmplot(data = wb, x = "per capita: % growth: 2016", \ y = "Adult literacy rate: Female: % ages 15 and older: 2005-14") plt.title("Female adult literacy against % growth");
/Users/Ishani/micromamba/lib/python3.9/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight
sns.jointplot
创建了一个可视化,包括三个组件:散点图、x 值分布的直方图和 y 值分布的直方图。
sns.jointplot(data = wb, x = "per capita: % growth: 2016", \ y = "Adult literacy rate: Female: % ages 15 and older: 2005-14") # plt.suptitle allows us to shift the title up so it does not overlap with the histogram plt.suptitle("Female adult literacy against % growth") plt.subplots_adjust(top=0.9);
8.1.0.3 六边形图
对于具有大量数据点的数据集,抖动不太可能完全解决重叠绘图的问题。在这种情况下,我们可以尝试通过密度来可视化我们的数据,而不是显示每个单独的数据点。
六边形图可以被看作是显示两个变量之间联合分布的二维直方图。在处理非常密集的数据时,这是特别有用的。在六边形图中,x-y 平面被分成六边形。颜色较深的六边形表示数据的密度更大 - 也就是说,在六边形所围区域内有更多的数据点。
我们可以使用带有kind
参数修改的sns.jointplot
生成六边形图。
sns.jointplot(data = wb, x = "per capita: % growth: 2016", \ y = "Adult literacy rate: Female: % ages 15 and older: 2005-14", \ kind = "hex") # plt.suptitle allows us to shift the title up so it does not overlap with the histogram plt.suptitle("Female adult literacy against % growth") plt.subplots_adjust(top=0.9);
8.1.0.4 等高线图
等高线图是绘制两个变量的联合分布的另一种方式。你可以把它们看作是 KDE 图的二维版本。等高线图可以类似于等高线地图。每条等高线代表一个具有相同数据密度的区域。深色标记的等高线包含更多的数据点(更高的密度)。
如果我们指定了 x 和 y 数据,sns.kdeplot
将生成等高线图。
sns.kdeplot(data = wb, x = "per capita: % growth: 2016", \ y = "Adult literacy rate: Female: % ages 15 and older: 2005-14", \ fill = True) plt.title("Female adult literacy against % growth");
8.2 转换
我们现在已经深入研究了可视化,涉及各种形式的可视化、绘图库和高层理论。
这些工作的大部分是为了发现数据中的见解,在课程后期构建数据模型时将证明是必要的。两个变量之间的强烈图形相关性暗示着一个我们可能想要更详细研究的潜在关系。然而,仅依赖于视觉关系是有限的 - 并非所有的图表都显示出关联。异常值和其他统计异常使得难以解释数据。
转换是操纵数据以找到变量之间显著关系的过程。通常通过将数学函数应用于“转换”它们的可能值范围,并突出一些以前隐藏的数据关联来找到这些关系。
要了解为什么我们可能需要转换数据,请考虑以下成年人识字率与国民总收入的图表。
代码
# Some data cleaning to help with the next example df = pd.DataFrame(index=wb.index) df['lit'] = wb['Adult literacy rate: Female: % ages 15 and older: 2005-14'] \ + wb["Adult literacy rate: Male: % ages 15 and older: 2005-14"] df['inc'] = wb['gni'] df.dropna(inplace=True) plt.scatter(df["inc"], df["lit"]) plt.xlabel("Gross national income per capita") plt.ylabel("Adult literacy rate") plt.title("Adult literacy rate against GNI per capita");
这个图很难解释,原因有两个:
- 可视化中显示的数据似乎几乎“挤压”在一起 - 它在图表的左上方区域非常集中。即使我们对数据集进行了抖动,我们可能也无法完全评估该区域内的所有数据点。
- 很难概括出两个变量之间的明确关系。虽然成年人识字率似乎与国民总收入有一些正向关系,但我们无法详细描述这一趋势的具体情况。
转换将使我们能够更清晰地可视化这些数据,从而使我们能够描述我们感兴趣的变量之间的潜在关系。
我们最常应用变换来线性化变量之间的关系。如果我们找到一个变换使得两个变量的散点图呈线性关系,我们可以“回溯”找到变量之间的确切关系。这在两个主要方面帮助我们。首先,线性关系特别容易解释 - 我们直观地知道线性趋势的斜率和截距代表什么,以及它们如何帮助我们理解两个变量之间的关系。其次,线性关系是线性模型的基础。我们将在下周开始详细探讨线性建模。正如我们将很快看到的,当我们处理线性化的数据时,线性模型变得更加有效。
在本笔记的其余部分,我们将讨论如何对数据集进行线性化,以产生下面的结果。请注意,所得的图显示了 x 和 y 轴上绘制的值之间的粗略线性关系。
8.2.1 线性化和应用变换
要线性化一个关系,请先问自己:是什么使数据非线性?对于可视化中的每个变量,重复这个问题是有帮助的。
让我们首先考虑上面图中的国民总收入变量。观察散点图中的 y 值,我们可以看到许多大的 y 值都聚集在一起,压缩了垂直轴。水平轴的比例也受到右侧少数大的离群 x 值的影响而被扭曲。
如果我们相对于大部分数据减小这些离群值的大小,我们可以减少水平轴的扭曲。我们如何做到这一点呢?我们需要一个变换,它将:
- 显著减小大 x 值的幅度。
- 不会大幅改变小 x 值的幅度。
产生这种结果的一个函数是对数变换。当我们取一个大数的对数时,原始数值的幅度会急剧减小。相反,当我们取一个小数的对数时,原始数值的值不会发生显著的变化(为了说明这一点,考虑一下log ( 100 ) = 4.61 \log{(100)} = 4.61log(100)=4.61和log ( 10 ) = 2.3 \log{(10)} = 2.3log(10)=2.3之间的差异)。
在 Data 100(以及大多数高年级的 STEM 课程)中,log \loglog用于指代以e ee为底的自然对数。
# np.log takes the logarithm of an array or Series plt.scatter(np.log(df["inc"]), df["lit"]) plt.xlabel("Log(gross national income per capita)") plt.ylabel("Adult literacy rate") plt.title("Adult literacy rate against Log(GNI per capita)");
在对 x 值取对数之后,我们的图在水平比例上显得更加平衡。我们不再有许多数据点聚集在一端,也没有少数离群值位于极端值。
让我们对 y 值重复这种推理。只考虑图的垂直轴,注意到有许多数据点集中在大的 y 值上。只有少数数据点位于较小的 y 值。
如果我们能够更加“分散”这些大的 y 值,我们将不再看到 y 轴的一个区域中有密集的集中。我们需要一个变换,它将:
- 增加 y 的大值的幅度,使这些数据点在垂直比例上更广泛地分布,
- 不要显著改变 y 的小值的比例(我们不希望大幅修改 y 轴的下限,因为它已经在垂直比例上均匀分布)。
在这种情况下,应用幂变换是有帮助的 - 也就是说,将我们的 y 值提高到一个幂。让我们尝试将成年识字率的值提高到 4 次方。大值提高到 4 次方会比小值提高到 4 次方增加更多的幅度(考虑2 4 = 16 2^4 = 1624=16和20 0 4 = 1600000000 200^4 = 16000000002004=1600000000之间的差异)。
# Apply a log transformation to the x values and a power transformation to the y values plt.scatter(np.log(df["inc"]), df["lit"]**4) plt.xlabel("Log(gross national income per capita)") plt.ylabel("Adult literacy rate (4th power)") plt.suptitle("Adult literacy rate (4th power) against Log(GNI per capita)") plt.subplots_adjust(top=0.9);
我们的散点图看起来好多了!现在,我们在水平轴上绘制原始 x 值的对数,垂直轴上绘制原始 y 值的 4 次方。我们开始看到我们转换变量之间的近似线性关系。
我们能从中得出什么?我们现在知道国民总收入的对数和成年识字率的 4 次方大致呈线性关系。如果我们将原始未转换的国民总收入值表示为 x xx,原始成年识字率值表示为 y yy,我们可以使用线性拟合的标准形式来表示这种关系:
y 4 = m ( log x ) + b y^4 = m(\log{x}) + by4=m(logx)+b
其中,m mm 表示线性拟合的斜率,b bb 表示截距。
下面的单元格计算了我们转换数据的 m mm 和 b bb。我们将在未来的讲座中讨论这段代码是如何生成的。
代码
# The code below fits a linear regression model. We'll discuss it at length in a future lecture from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(np.log(df[["inc"]]), df["lit"]**4) m, b = model.coef_[0], model.intercept_ print(f"The slope, m, of the transformed data is: {m}") print(f"The intercept, b, of the transformed data is: {b}") df = df.sort_values("inc") plt.scatter(np.log(df["inc"]), df["lit"]**4, label="Transformed data") plt.plot(np.log(df["inc"]), m*np.log(df["inc"])+b, c="red", label="Linear regression") plt.xlabel("Log(gross national income per capita)") plt.ylabel("Adult literacy rate (4th power)") plt.legend();
The slope, m, of the transformed data is: 336400693.43172693 The intercept, b, of the transformed data is: -1802204836.0479977
如果我们想要了解在转换之前我们原始变量之间的潜在关系呢?我们可以简单地重新排列上面的线性表达式!
回想一下我们转换变量 log x \log{x}logx 和 y 4 y^4y4 之间的线性关系。
y 4 = m ( log x ) + b y^4 = m(\log{x}) + by4=m(logx)+b
通过重新排列方程,我们找到了未转换变量 x xx 和 y yy 之间的关系。
y = [ m ( log x ) + b ] ( 1 / 4 ) y = [m(\log{x}) + b]^{(1/4)}y=[m(logx)+b](1/4)
当我们代入上面计算的 m mm 和 b bb 的值时,有趣的事情发生了。
代码
# Now, plug the values for m and b into the relationship between the untransformed x and y plt.scatter(df["inc"], df["lit"], label="Untransformed data") plt.plot(df["inc"], (m*np.log(df["inc"])+b)**(1/4), c="red", label="Modeled relationship") plt.xlabel("Gross national income per capita") plt.ylabel("Adult literacy rate") plt.legend();
我们已经找到了我们原始变量——国民总收入和成年识字率之间的关系!
转换是了解我们的数据更详细的强大工具。总结我们刚刚取得的成果:
- 我们确定了适当的转换方法来线性化原始数据。
- 我们利用我们对线性曲线的了解来计算转换数据的斜率和截距。
- 我们使用这个斜率和截距信息来推导未转换数据中的关系。
线性化将是我们下周开始线性建模工作的重要工具。
8.2.1.1 图基-莫斯特勒凸起图
图基-莫斯特勒凸起图是确定可能的转换以实现线性关系时的良好指南。它是我们刚刚通过的推理的视觉总结。
它是如何工作的?每个弯曲的“凸起”代表非线性数据的可能形状。要使用该图,找出四个凸起中哪一个最接近你的数据集。然后,查看该凸起的象限轴。水平轴将列出可能应用于 x 数据线性化的转换。同样,垂直轴将列出可能应用于 y 数据的转换。请注意,每个轴列出两种可能的转换。虽然任何这些转换都有潜力使您的数据集线性化,但请注意这是一个迭代过程。重要的是要尝试这些转换并查看结果,以查看您是否实际实现了线性关系。如果没有,您将需要继续测试其他可能的转换。
一般来说:
- \sqrt{} 和 log \log{}log 将减少大值的幅度。
- 幂次(2 ^22 和 3 ^33)将增加大值的幅度。
重要: 您仍应理解我们通过的逻辑,以确定如何最好地转换数据。凸起图只是对这种推理的总结。您应该能够解释为什么给定的转换是否适合线性化。
8.2.2 附加说明
可视化需要大量思考!
- 有许多用于可视化分布的工具。
- 单个变量的分布:
- Rugplot
- 直方图
- 密度图
- 箱线图
- 小提琴图
- 两个定量变量的联合分布:
- 散点图
- 六边形图
- 等高线图
这门课程主要使用seaborn
和matplotlib
,但pandas
也有基本的内置绘图方法。还有许多其他可视化库,其中plotly
就是其中之一。
plotly
非常容易创建交互式图。plotly
偶尔会出现在讲座代码、实验和作业中!
接下来,我们将深入探讨可视化背后的理论。
8.3 可视化理论
本节标志着本讲座的第二个主要主题-可视化理论。我们将讨论可视化的抽象性质,并分析它们如何传达信息。
记住,我们对可视化数据有两个目标。本节在以下方面尤为重要:
- 帮助我们理解数据和结果,
- 与他人交流我们的结果和结论。
- UCB Data100:数据科学的原理和技巧:第六章到第十章(5)https://developer.aliyun.com/article/1427172