数据探索和可视化(Data exploration and visualisation)

520 阅读9分钟

探索和可视化毕业率数据集(Exploring and visualising the graduation rate dataset)

为了展示数据探索和数据可视化的典型Python功能,我们将使用毕业率数据集(roycekimmons.com/tools/gener…

1.1 载入毕业率数据集(Loading the graduation rate dataset)

毕业率数据集存储在一个名为graduation_rate.csv的文件中,该文件可与本笔记本一起找到。你可以用任何文本编辑器查看这个文件。该文件包含1001行。第一行包含特征的名称,用逗号分隔。其余的行包含每行一个观测值。每个观测值的特征值也用行分开。 库pandas有一个方便的函数,叫做read_csv,它期待一个遵循上述约定的文件。这个函数返回一个DataFrame,代表数据集。 在笔记本中使用的函数display类似于Python函数print,但以一种更方便的格式显示DataFrame。默认情况下,只显示一个大的DataFrame的前五行和后五行。 通过这个初步的检查可以发现,除了父母教育程度这个特征有多个文本值外,大部分特征都是数字型的。为了显示该特征的有效值,我们从DataFrame中选择相应的列,并使用Series.unique方法。该方法返回整个Series的唯一值。

# Loading dataset (http://roycekimmons.com/tools/generated_data)
import pandas as pd

df = pd.read_csv('graduation_rate.csv')

print('Dataset (head and tail):')
display(df)

print('\nParental levels of education:')
print(df['parental level of education'].unique())

1.2 预处理特点(Pre-processing features)

为了对父母的教育水平进行自然排序,数据框架的相应列由使用函数pd.Categorical创建的序数特征列代替。

education_order = ['some high school', 'high school', 'some college', "associate's degree", "bachelor's degree", "master's degree"]

df['parental level of education'] = pd.Categorical(df['parental level of education'],
                                                   ordered=True,
                                                   categories=education_order)

display(df['parental level of education'])
df['parental level of education'].max()

1.3 数据汇总(Data summarisation)

DataFrame.describe这个方法可以用来计算我们在讲座中讲到的大部分单变量摘要。对于每个特征,该方法计算平均值、标准差、最小值、最大值、中值、下四分位数和上四分位数。 请注意,DataFrame.describe会检测并省略分类特征父母的教育水平,因为这些总结不会有用。相反,可以使用Series.value_counts方法来推导出这个特征的每个值的频率。 DataFrame.corr方法可以用来计算数据集中的(数字)特征的相关矩阵。

print('Univariate summaries:')
display(df.describe())
print("Frequency of parental levels of education:")
freq_education = df['parental level of education'].value_counts()/len(df)
display(freq_education)

print("\nCorrelation coefficients:")
display(df.corr())

1.4 可视化表(Table visualisation)

DataFrame.sort_values方法可以用来按升序或降序,根据特定特征的值对DataFrame的行进行排序。例如,我们可以使用这个方法按大学GPA对学生进行排序。 我们还可以使用分片法从DataFrame中选择一系列的观测值。例如,我们可以按照父母收入的增加对学生进行排序,然后选择前十个学生(父母收入最低)。 当DataFrame被一个布林值列表索引时,只有对应于True值的行才会被返回。当用于索引的列表由Series表示时,该功能也可以使用。该功能可用于根据特征选择通过测试的行。例如,我们可以用它来选择父母受教育程度在高中以上的学生。 DataFrame.groupby方法提供了一种简单的方法,可以根据所选分类特征的值(例如,父母的受教育程度)将观测值划分成组,并对每个组进行独立操作(例如,为每个组独立计算数值特征的平均值)。

#print(df.to_latex()) # Print a table for use with LaTeX
print('Sorting by college gpa:')
display(df.sort_values(by='college gpa', ascending=False))

print('Selecting the ten students with lowest parental income :')
display(df.sort_values(by='parental income', ascending=True)[0:10])

print('Sorting by high school gpa the students whose parents are educated beyond high school')
# Note that a boolean sequence can be used to index a DataFrame
display(df[df['parental level of education'] > 'high school'].sort_values(by='high school gpa', ascending=False))

print('Grouping by parental level of education and computing the mean for other features:')
display(df.groupby('parental level of education').mean())

1.5 直方图

库seaborn为使用matplotlib绘制吸引人的图形提供了一个高级接口。 以下代码段用于配置本笔记本中seaborn图形的外观。

%config InlineBackend.figure_formats = set(['retina'])

import seaborn as sns
import matplotlib.pyplot as plt

sns.set_style('darkgrid')

函数distplot可以用来为DataFrame(或任何数字列表)的给定列创建一个具有指定数量的直方块的直方图。例如,它可以用来创建高中gpa和大学gpa的直方图。

sns.distplot(df['high school gpa'], bins=None, kde=False)
plt.title('Histogram: high school gpa')
plt.show()

sns.distplot(df['college gpa'], bins=None, kde=False)
plt.title('Histogram: college gpa')
plt.show()

1.6 饼图

库seaborn目前没有创建饼图的功能(可能是因为这种类型的可视化通常不被鼓励)。matplotlib函数pie(matplotlib.pyplot.pie)可以用来用饼图来描述一个频率数组。例如,我们可以显示在数据汇总部分计算的父母教育水平的频率。

plt.pie(freq_education, labels=freq_education.index)
plt.title('Pie chart: parental level of education')
plt.show()

1.7 箱形图

函数boxplot可以用来创建一个特定特征的箱形图,例如,它可以用来创建父母收入的箱形图。例如,它可以用来创建父母收入的箱形图。

sns.boxplot(df['parental income'], orient='v')
plt.title('Boxplot: parental income')
plt.show()

函数boxplot还能够按分类特征对观测值进行分组,并为每个分组创建一个框图。例如,我们可以为每一个父母的教育水平创建一个父母收入的方框图。

ax = sns.boxplot(x='parental level of education', y='parental income', data=df)
plt.title('Boxplot: parental income, grouped by parental level of education')

# Wrap xticks 
import textwrap
ax.set_xticklabels([textwrap.fill(t.get_text(), 10)  for t in ax.get_xticklabels()])

plt.show()

1.8 散点图

函数scatterplot可以用来创建任意给定特征对的散点图,而函数pairplot可以用来创建散点图矩阵。 根据参数hue给出的分类特征,可以对结果点进行着色。

sns.scatterplot(x='ACT composite score', y='SAT total score', data=df)
plt.show()

sns.pairplot(df, hue='parental level of education', diag_kind='hist')
plt.show()

1.9 矩阵距离(Distance matrices)

为了使距离矩阵可视化,通常重要的是按照给定的分类特征对数据集中的观测值进行分组。 例如,我们可以按照父母教育水平的提高对观测值进行分类。因为父母教育水平这个特征是序数而不是数值,所以我们也可以决定在计算观测值之间的距离时将其从考虑中删除。 此外,在计算距离时,对不同的特征进行缩放,使其幅度具有可比性,这一点总是很重要的。例如,我们可以使用sklearn类StandardScaler分别对每个特征进行标准化。StandardScaler.fit_transform方法期望得到一个包含各行观测值的numpy矩阵,并返回一个相应的具有标准化特征的矩阵。

from sklearn.preprocessing import StandardScaler

df_sorted = df.sort_values(by='parental level of education', ascending=True)
parental_education_sorted = df_sorted['parental level of education']

X = df_sorted.drop(columns='parental level of education').to_numpy()
scaler = StandardScaler()
X = scaler.fit_transform(X)

scipy函数pdist(scipy.spatial.distance.pdist)可以用来计算矩阵中观测值之间的成对欧几里得距离,而函数squareform(scipy.spatial.distance.squareform)可以用来将pdist的返回值转换成我们期望的表示方式(pdist返回的是对称矩阵的浓缩表示方式)。 最后,seaborn函数heatmap可以用来为相应的距离矩阵创建一个热图(对于一个选定的colormap)。

from scipy.spatial import distance

dist = distance.squareform(distance.pdist(X))
sns.heatmap(dist, square=True, xticklabels=False, yticklabels=False,
                cmap='Blues')
#plt.figure(figsize=(15,10))    
plt.show()

1.10 减少维度(Dimensionality reduction)

库sklearn中的类MDS通过多维缩放实现了维度降低。如上例中用于计算距离矩阵的标准化观测值矩阵是MDS.fit_transform方法的合适输入,该方法输出的矩阵包含输入矩阵中每个观测值的二维点。可以用散点图来描述这个输出矩阵。

from sklearn.manifold import MDS

embedding = MDS(n_components=2)

Xp = embedding.fit_transform(X)

df_projection = pd.DataFrame({'x': Xp[:, 0], 'y': Xp[:, 1],
                              'parental level of education': parental_education_sorted})

sns.scatterplot(x='x', y='y', hue='parental level of education', data=df_projection)

plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)

plt.show()

库sklearn中的类TSNE通过t-分布式随机邻域嵌入(t-SNE)实现了维度降低。其接口类似于MDS类提供的接口。

from sklearn.manifold import TSNE

embedding = TSNE(n_components=2, perplexity=100)

Xp = embedding.fit_transform(X)
df_projection = pd.DataFrame({'x': Xp[:, 0], 'y': Xp[:, 1],
                              'parental level of education': parental_education_sorted})
sns.scatterplot(x='x', y='y', hue='parental level of education', data=df_projection)

plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.show()

在下面的例子中,通过t-SNE得到的每一个点,都会根据对应学生的父母是否具有高等教育学历而着色。

df_projection['parents have degree'] = (df['parental level of education'] > 'some college')
sns.scatterplot(x='x', y='y', hue='parents have degree', data=df_projection)

plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.show()

分析函数的可视化(Visualising an analytic function)

为了介绍与可视化标量场和矢量场相关的Python功能,我们将使用由𝑓(𝑥,𝑦)=𝑧=𝑥2+𝑦2给出的分析函数𝑓:ℝ2→ℝ作为工作实例。 numpy函数linspace可以用来创建一个在指定区间内均匀排列的数字列表,而函数meshgrid可以用来从两个给定的数字列表中创建所有可能的数字组合。 在我们的例子中,函数meshgrid返回两个矩阵。第一个矩阵在行间复制第一个列表的数字。在我们的例子中,这个矩阵代表沿x轴的位置。第二个矩阵在列上复制第二个列表的数字。在我们的例子中,这个矩阵代表沿y轴的位置。 通过应用最终结合两个矩阵的元素运算,可以在由linspace生成的两个数字列表所定义的网格的每个元素上评估两个变量的函数。产生的数据集也可以用DataFrame表示。

import numpy as np
x_range = np.linspace(-1, 1, 10)
y_range = np.linspace(-1, 1, 10)

# meshgrid: X[i, j] == x_range[j] and Y[i, j] == y_range[i]
X, Y = np.meshgrid(x_range, y_range)

# Z[i, j] == f(x_range[j], y_range[i])
Z = X**2 + Y**2

# Dataset representation
df = pd.DataFrame({'x': X.reshape(-1), 'y': Y.reshape(-1), 'z = f(x,y)': Z.reshape(-1)})
display(df)

2.1 热力图

matplotlib函数imshow可以用来通过最近邻插值来创建热图。

# Interpolation: point (x, y) is colored according to the value z of the nearest point in the dataset
plt.imshow(Z, cmap='Blues', aspect='equal', interpolation='nearest')
plt.colorbar()

# xticks and yticks would show Z matrix indices
plt.xticks([])
plt.yticks([])

plt.show()

matplotlib函数imshow也可以用来通过双线插值创建热图。

# Interpolation: point (x, y) is colored according to the (weighted average) value z of the four nearest points
plt.imshow(Z, cmap='Blues', aspect='equal', interpolation='bilinear')
plt.colorbar()

# xticks and yticks would show Z matrix indices
plt.xticks([])
plt.yticks([])

plt.show()

2.2 轮廓螺栓( Contour plots)

matplotlib函数contour可以用来创建等高线图。

CS = plt.contour(X, Y, Z, levels=10, cmap='Blues')
plt.clabel(CS, inline=True, fontsize=10)
plt.show()

2.3 表面图(Surface plots)

函数库matplotlib也能够创建(交互式)三维图,可以使用函数plot_surface创建曲面图。可以使用函数plot_surface创建曲面图。

from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')

surf = ax.plot_surface(X, Y, Z, cmap='Blues', linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=10)

plt.show()

2.4 quiver plots

matplotlib函数quiver可以用来创建quiver图。例如,我们可以使用numpy函数gradient通过有限差分法来逼近标量场𝑓的梯度函数∇𝑓,然后可以用quiver图来表示。

DY, DX = np.gradient(Z)
plt.quiver(X, Y, DX, DY)
plt.show()