关系(一)利用python绘制散点图
散点图 (Scatterplot)简介
在笛卡尔座标上放置一系列的数据点,检测两个变量之间的关系,这就是散点图。
散点图可以了解数据之间的各种相关性,如正比、反比、无相关、线性、指数级、 U形等,而且也可以通过数据点的密度(辅助拟合趋势线)来确定相关性的强度。另外,也可以探索出异常值(在远超出一般聚集区域的数据点称)。
快速绘制
-
基于seaborn
import seaborn as sns import matplotlib.pyplot as plt # 导入数据 df = sns.load_dataset('iris') # 基于scatterplot函数绘制散点图 sns.scatterplot(x=df["sepal_length"], y=df["sepal_width"]) plt.show()
-
基于matplotlib
import seaborn as sns import matplotlib.pyplot as plt # 导入数据 df = sns.load_dataset('iris') # 基于plot函数绘制散点图 plt.plot( 'sepal_length', 'sepal_width', data=df, linestyle='none', marker='o') plt.show()
定制多样化的散点图
自定义散点图一般是结合使用场景对相关参数进行修改,并辅以其他的绘图知识。参数信息可以通过官网进行查看,其他的绘图知识则更多来源于实战经验,大家不妨将接下来的绘图作为一种学习经验,以便于日后总结。
通过seaborn绘制多样化的散点图
seaborn主要利用scatterplot
和regplot
绘制散点图,可以通过seaborn.scatterplot和seaborn.regplot了解更多用法
-
修改参数
import seaborn as sns import matplotlib.pyplot as plt import numpy as np sns.set(font='SimHei', font_scale=0.8, style="darkgrid") # 解决Seaborn中文显示问题 # 导入数据 df = sns.load_dataset("iris") # 构造子图 fig, ax = plt.subplots(2,2,constrained_layout=True, figsize=(8, 8)) # 增加趋势拟合线 ax_sub = sns.regplot(x=df["sepal_length"], y=df["sepal_width"], fit_reg=True, line_kws={"color":"r","alpha":0.7,"lw":5},ax=ax[0][0]) ax_sub.set_title('增加趋势拟合线') # 自定义标记类型 ax_sub = sns.regplot(x=df["sepal_length"], y=df["sepal_width"], marker="+", fit_reg=False, ax=ax[0][1]) ax_sub.set_title('自定义标记类型') # 自定义标记外形 ax_sub = sns.regplot(x=df["sepal_length"], y=df["sepal_width"], fit_reg=False, scatter_kws={"color":"darkred", # 颜色 "alpha":0.3, # 透明度 "s":200}, # 点大小 ax=ax[1][0] ) ax_sub.set_title('自定义标记外形') # 自定义每个点颜色 value=(df['sepal_length']>6) & (df['sepal_width']>3) # 构造特殊的点 df['color']= np.where( value==True , "#9b59b6", "#3498db") # 颜色区分 ax_sub = sns.regplot(data=df, x=df["sepal_length"], y=df["sepal_width"], fit_reg=False, scatter_kws={'facecolors':df['color']},ax=ax[1][1]) ax_sub.set_title('自定义每个点颜色') plt.show()
-
分组散点图
import matplotlib.pyplot as plt import seaborn as sns import matplotlib.gridspec as gridspec # 导入自定义模块 import SeabornFig2Grid as sfg sns.set(font='SimHei', font_scale=0.8, style="darkgrid") # 解决Seaborn中文显示问题 # 导入数据 df = sns.load_dataset('iris') fig = plt.figure(figsize=(8, 8)) gs = gridspec.GridSpec(2, 2) # 默认的分组散点图 scatter1 =sns.lmplot( x="sepal_length", y="sepal_width", data=df, fit_reg=False, hue='species', legend=False) scatter1.ax.set_title('默认的分组散点图') # 自定义每组的标记 scatter2 = sns.lmplot( x="sepal_length", y="sepal_width", data=df, fit_reg=False, hue='species', legend=False, markers=["o", "x", "1"]) scatter2.ax.set_title('自定义每组的标记') # 自定义调色板 scatter3 = sns.lmplot( x="sepal_length", y="sepal_width", data=df, fit_reg=False, hue='species', legend=False, palette="Set2") scatter3.ax.set_title('自定义调色板') # 自定义颜色 scatter4 = sns.lmplot( x="sepal_length", y="sepal_width", data=df, fit_reg=False, hue='species', legend=False, palette=dict(setosa="#9b59b6", virginica="#3498db", versicolor="#95a5a6")) scatter4.ax.set_title('自定义颜色') # 使用SeabornFig2Grid转换 seaborn 图为 matplotlib 子图 mg1 = sfg.SeabornFig2Grid(scatter1, fig, gs[0]) mg2 = sfg.SeabornFig2Grid(scatter2, fig, gs[1]) mg3 = sfg.SeabornFig2Grid(scatter3, fig, gs[2]) mg4 = sfg.SeabornFig2Grid(scatter4, fig, gs[3]) gs.tight_layout(fig) plt.show()
-
添加文本注释
import pandas as pd import numpy as np import matplotlib.pylab as plt import seaborn as sns # 自定义数据 df = pd.DataFrame({ 'x': [1, 1.5, 3, 4, 5], 'y': [5, 15, 5, 10, 2], 'group': ['A','other group','B','C','D'] }) # 绘制基本散点图 sns.regplot(data=df, x="x", y="y", fit_reg=False, marker="o", color="skyblue", scatter_kws={'s':400}) # 为每个点添加注释 for line in range(0,df.shape[0]): plt.text(df.x[line]+0.2, df.y[line], df.group[line], horizontalalignment='left', size='medium', color='black', weight='semibold') plt.show()
通过matplotlib绘制多样化的散点图
matplotlib主要利用plot
绘制散点图,可以通过matplotlib.pyplot.plot了解更多用法
-
自定义散点图
import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.patches as patches import numpy as np import pandas as pd import palmerpenguins mpl.rcParams.update(mpl.rcParamsDefault) # 恢复默认的matplotlib样式 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 # 设置随机种子 np.random.seed(0) # 自定义数据 df=pd.DataFrame({'x_pos': range(1,101), 'y_pos': np.random.randn(100)*80+range(1,101) }) # 初始化 fig = plt.figure(figsize=(12,8)) # 1、自定义标记 ax = plt.subplot2grid((2, 2), (0, 0), colspan=1) plt.plot( 'x_pos', 'y_pos', data=df, linestyle='none', marker='*', # 标记形状 markersize=12, # 标记大小 markerfacecolor='skyblue', # 标记颜色 markeredgewidth=0.3, # 标记边缘 markeredgecolor="orange", # 标记边缘颜色 ) ax.set_title('自定义标记') # 2、添加注释信息 ax = plt.subplot2grid((2, 2), (0, 1), colspan=1) plt.plot( 'x_pos', 'y_pos', data=df, linestyle='none', marker='o') # 文本 plt.annotate('有趣的点!', xy=(25, 200), xytext=(0, 300), # 自定义箭头 arrowprops=dict(facecolor='black', shrink=0.005)) # 公式 plt.text(70, -120, r'equation: $\sum_{i=0}^\infty x_i$', fontsize=12) # 正方形边缘框 # patches.Circle为圆形边框,patches.Ellipse为椭圆形边框 ax.add_patch( patches.Rectangle( (70, 100), # (x,y) 25, # 宽 50, # 高 alpha=0.3, facecolor="red", edgecolor="black", linewidth=3, linestyle='solid') ) # 坐标线(水平与垂直线) plt.axvline(40, color='r'),plt.axhline(40, color='r') ax.set_title('添加注释信息') # 3、自定义图例 # 导入数据 data = palmerpenguins.load_penguins() # 数据定义 FLIPPER_LENGTH = data["flipper_length_mm"].values # flipper长度数据 BILL_LENGTH = data["bill_length_mm"].values # bill长度数据 SPECIES = data["species"].values # 物种类别数据 SPECIES_ = np.unique(SPECIES) # 物种唯一值 COLORS = ["#1B9E77", "#D95F02", "#7570B3"] # 颜色列表 ax = plt.subplot2grid((2, 2), (1, 0), colspan=1) for species, color in zip(SPECIES_, COLORS): idxs = np.where(SPECIES == species) ax.scatter( FLIPPER_LENGTH[idxs], BILL_LENGTH[idxs], label=species, s=50, color=color, alpha=0.7 ) # 位置、大小等图例参数 legend = ax.legend(loc="lower right", fontsize=6, markerscale=2, labelspacing=2, frameon=False) handles = legend.legendHandles # 自定义标识 hatches = ["+", "x", "o"] for i, handle in enumerate(handles): handle.set_edgecolor("#6c2167") handle.set_facecolor(COLORS[i]) handle.set_hatch(hatches[i]) handle.set_alpha(0.7) ax.set_title('自定义图例') # 4、回归拟合线 # 自定义数据 rng = np.random.default_rng(1234) x = rng.uniform(0, 10, size=100) y = x + rng.normal(size=100) # 绘制散点图 ax = plt.subplot2grid((2, 2), (1, 1), colspan=1) ax.scatter(x, y, s=60, alpha=0.7, edgecolors="k") # 拟合-polyfit返回斜率b和截距a b, a = np.polyfit(x, y, deg=1) # 绘制回归线 xseq = np.linspace(0, 10, num=100) # x轴 ax.plot(xseq, a + b * xseq, color="k", lw=2.5) ax.set_title('回归拟合线') plt.show()
-
对数刻度线
import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.patches as patches import numpy as np import pandas as pd plt.rcParams['font.sans-serif'] = ['DejaVu Sans'] # 用来正常显示'-' [U+2212]问题 # 自定义数据 rng = np.random.default_rng(1234) x = rng.lognormal(size=200) y = x + rng.normal(scale=5 * (x / np.max(x)), size=200) # 初始化 fig = plt.figure(figsize=(10,6)) # 原始散点图 ax = plt.subplot2grid((2, 2), (0, 0), colspan=1) ax.scatter(x, y, s=60, alpha=0.7, edgecolors="k") ax.set_title('default') # x轴对数刻度 ax = plt.subplot2grid((2, 2), (0, 1), colspan=1) ax.scatter(x, y, s=60, alpha=0.7, edgecolors="k") ax.set_xscale("log") ax.set_title('logarithmic scale for x axes') # y轴对数刻度 ax = plt.subplot2grid((2, 2), (1, 0), colspan=1) ax.scatter(x, y, s=60, alpha=0.7, edgecolors="k") ax.set_yscale("log") ax.set_title('logarithmic scale for y axes') # xy轴对数刻度 ax = plt.subplot2grid((2, 2), (1, 1), colspan=1) ax.scatter(x, y, s=60, alpha=0.7, edgecolors="k") ax.set_xscale("log") ax.set_yscale("log") ax.set_title('logarithmic scale for x/y axes') fig.tight_layout() # 自动调整间距 plt.show()
-
引申-绘制曼哈顿图
# 曼哈顿图是散点图的一种变体,可联想曼哈顿鳞次栉比的大楼 # 一般用于基因相关研究,如GWAS。每组表示一个染色体,每个点表示一个基因 # x轴为该点在染色体的位置,y轴值代表其P值的-log10,越高相关性越强 from pandas import DataFrame from scipy.stats import uniform from scipy.stats import randint import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import matplotlib.cm as cm mpl.rcParams.update(mpl.rcParamsDefault) # 恢复默认的matplotlib样式 # 自定义数据 df = DataFrame({'gene' : ['gene-%i' % i for i in np.arange(10000)], 'pvalue' : uniform.rvs(size=10000), 'chromosome' : ['ch-%i' % i for i in randint.rvs(0,12,size=10000)]}) # p值对数化 df['minuslog10pvalue'] = -np.log10(df.pvalue) # 染色体组 df.chromosome = df.chromosome.astype('category') # 转category类型 df.chromosome = df.chromosome.cat.set_categories(['ch-%i' % i for i in range(12)], ordered=True) # 排序 df = df.sort_values('chromosome') # 数据排序 # 如何绘制基因与 -log10(pvalue) 的关系并按染色体着色 df['ind'] = range(len(df)) df_grouped = df.groupby(('chromosome')) # 绘制曼哈顿图 fig = plt.figure(figsize=(14, 8)) ax = fig.add_subplot(111) cmap = cm.get_cmap('rainbow', 12) # 获取调色板 colors = [cmap(i) for i in range(12)] # 分配颜色 x_labels = [] x_labels_pos = [] for num, (name, group) in enumerate(df_grouped): group.plot(kind='scatter', x='ind', y='minuslog10pvalue',color=colors[num % len(colors)], ax=ax) x_labels.append(name) x_labels_pos.append((group['ind'].iloc[-1] - (group['ind'].iloc[-1] - group['ind'].iloc[0])/2)) ax.set_xticks(x_labels_pos) ax.set_xticklabels(x_labels) # 设置轴 ax.set_xlim([0, len(df)]) ax.set_ylim([0, 3.5]) ax.set_xlabel('Chromosome') plt.show()
-
引申-单轴散点图
import matplotlib.pyplot as plt import numpy as np import pandas as pd import random # 自定义数据 days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] hours = list(range(24)) sales_data = [] for day in days: for hour in hours: rand_num = random.random() # 生成0-1之间的随机数 if rand_num <= 0.8: # 80%的概率生成0-100之间的销售量 sales_volume = random.randint(0, 20) else: # 20%的概率生成100-600之间的销售量 sales_volume = random.randint(40, 500) sales_data.append([day, hour, sales_volume]) df = pd.DataFrame(sales_data, columns=['Day', 'Hour', 'Sales Volume']) # 将一周的数据映射成数值类型,方便Y轴显示 day_to_num = {day: num for num, day in enumerate(days)} df['Day_num'] = df['Day'].map(day_to_num) # 创建一个新的figure plt.figure(figsize=(10,6)) # 移除边框 spines = plt.gca().spines for side in ['top','right']: spines[side].set_visible(False) # 为每一天绘制散点图 for day in days: day_data = df[df['Day'] == day] plt.scatter(day_data['Hour'], day_data['Day_num'], s=day_data['Sales Volume'], label=day) plt.yticks(np.arange(len(days)), days) # 设置y轴的刻度和标签 # 自定义图里 plt.legend(bbox_to_anchor=(1.02,1), loc="upper left", borderaxespad=0) plt.show()
如何避免过度绘制造成的散点重叠
适当处理样本
# 当数据集较大时,绘制散点图容易出现重叠造成不可读
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import seaborn as sns
import pandas as pd
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
# 自定义数据
df=pd.DataFrame({'x': np.random.normal(10, 1.2, 20000),
'y': np.random.normal(10, 1.2, 20000), 'group': np.repeat('A',20000) })
tmp1=pd.DataFrame({'x': np.random.normal(14.5, 1.2, 20000),
'y': np.random.normal(14.5, 1.2, 20000), 'group': np.repeat('B',20000) })
tmp2=pd.DataFrame({'x': np.random.normal(9.5, 1.5, 20000),
'y': np.random.normal(15.5, 1.5, 20000), 'group': np.repeat('C',20000) })
df=pd.concat([df,tmp1,tmp2], ignore_index=True)
# 初始化
fig = plt.figure(figsize=(8,8))
# 过度绘制
ax = plt.subplot2grid((2, 2), (0, 0), colspan=1)
plt.plot('x', 'y', "o", data=df, linestyle='')
ax.set_title('过度绘制')
# 减小标记与透明度
ax = plt.subplot2grid((2, 2), (0, 1), colspan=1)
plt.plot( 'x', 'y', "o", data=df, linestyle='', markersize=3, alpha=0.05, color="purple")
ax.set_title('减小标记与透明度')
# 2D密度图
ax = plt.subplot2grid((2, 2), (1, 0), colspan=1)
sns.kdeplot(data = df, x="x", y="y", cmap="Reds", fill=True)
ax.set_title('2D密度图')
# 样本采样
df_sample=df.sample(1000) # 样本采样
ax = plt.subplot2grid((2, 2), (1, 1), colspan=1)
plt.plot('x', 'y', "o", data=df_sample, linestyle='')
ax.set_title('样本采样')
fig.tight_layout() # 自动调整间距
plt.show()
分开观察
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib.gridspec as gridspec
# 导入自定义模块
import SeabornFig2Grid as sfg
sns.set(font='SimHei', font_scale=0.8, style="darkgrid") # 解决Seaborn中文显示问题
# 自定义数据
df=pd.DataFrame({'x': np.random.normal(10, 1.2, 20000),
'y': np.random.normal(10, 1.2, 20000), 'group': np.repeat('A',20000) })
tmp1=pd.DataFrame({'x': np.random.normal(14.5, 1.2, 20000),
'y': np.random.normal(14.5, 1.2, 20000), 'group': np.repeat('B',20000) })
tmp2=pd.DataFrame({'x': np.random.normal(9.5, 1.5, 20000),
'y': np.random.normal(15.5, 1.5, 20000), 'group': np.repeat('C',20000) })
df=pd.concat([df,tmp1,tmp2], ignore_index=True)
# 子图, 使用 gridspec 进行比例分配
fig = plt.figure(figsize=(8, 8))
gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1])
# 绘制分组散点图
scatter1 = sns.lmplot( x="x", y="y", data=df, fit_reg=False, hue='group',
legend=False, palette="Accent", scatter_kws={"alpha":0.1,"s":15} )
scatter1.ax.legend(loc='lower right', markerscale=2)
scatter1.ax.set_title('分组散点图')
# 绘制分位面散点图
g = sns.FacetGrid(df, col="group", hue="group")
scatter2 = (g.map(plt.scatter, "x", "y", edgecolor="w"))
g.set_titles('分位面散点图-group {col_name}')
# 使用SeabornFig2Grid转换 seaborn 图为 matplotlib 子图
mg1 = sfg.SeabornFig2Grid(scatter1, fig, gs[0])
mg2 = sfg.SeabornFig2Grid(scatter2, fig, gs[1])
gs.tight_layout(fig)
plt.show()
抖动散点图
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import pandas as pd
sns.set(font='SimHei', font_scale=0.8, style="darkgrid") # 解决Seaborn中文显示问题
# 自定义数据
a=np.concatenate([np.random.normal(2, 4, 1000),
np.random.normal(4, 4, 1000),
np.random.normal(1, 2, 500),
np.random.normal(10, 2, 500),
np.random.normal(8, 4, 1000),
np.random.normal(10, 4, 1000)])
df=pd.DataFrame({'x': np.repeat( range(1,6), 1000), 'y': a })
fig, axs = plt.subplots(ncols=2, figsize=(8, 4)) # 创建1行2列的子图
# 原始散点图
axs[0].plot( 'x', 'y', "o", data=df, linestyle='')
axs[0].set_title('原始散点图')
# 抖动散点图
sns.stripplot(data =df, x="x", y="y", jitter=0.2, size=2, ax=axs[1])
axs[1].set_title('抖动散点图')
plt.tight_layout()
plt.show()
3D图
# 3D图
from scipy.stats import gaussian_kde
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams.update(mpl.rcParamsDefault) # 恢复默认的matplotlib样式
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
# 自定义数据
nbins=300
k = gaussian_kde([df.x,df.y])
xi, yi = np.mgrid[ df.x.min():df.x.max():nbins*1j, df.y.min():df.y.max():nbins*1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
data=pd.DataFrame({'x': xi.flatten(), 'y': yi.flatten(), 'z': zi })
# 绘制3D图
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(data.x, data.y, data.z, cmap=plt.cm.Spectral, linewidth=0.2)
ax.view_init(30, 80)
plt.title('3D图')
plt.show()
总结
以上通过seaborn的scatterplot
和matplotlib的plot
可以快速绘制散点图,并通过修改参数或者辅以其他绘图知识自定义各种各样的散点图来适应相关使用场景。
共勉~