数据分析系列之数据探索

根据观测,调查收集到的初步的样本数据集后,接下来要考虑的是样本的数据集的数量和质量是否满足模型构建的要求,是否出现从未设想过的数据状态?其中有什么明显的规律趋势,各因素之间有什么关联性?通过检验数据集的数据质量,绘制图表,计算某些特征量等手段,对样本数据进行规律性分析的过程就是数据探索.数据探索主要包括数据质量分析和数据特征分析.

1.数据质量分析

数据质量分析主要是检查原始数据中是否存在脏数据,脏数据是指不符合要求,不能直接进行相应的数据分析的数据.脏数据主要包括以下数据:缺失值,异常值,不一致值,重复数据,含有特殊符号的数据等.

1.1 缺失值分析

缺失值主要包括记录的缺失和记录中某个字段的缺失,两者都会造成数据分析结果的不准确.以下从缺失值产生的原因和影响进行分析.
缺失值产生原因:

  1. 有些信息暂时无法获取或者获取信息的代价太大.
  2. 有些信息是被遗漏的,可能是人为因素或者机器原因.
  3. 属性值不存在,例如儿童不具有收入.
    缺失值的影响:
  4. 数据挖掘丢失大量有用信息.
  5. 数据挖掘建模表现出更大的不确定性.
  6. 包含空值导致数据建模过程陷入混乱,出现不可靠输出.
    所以对缺失值处理包括删除存在缺失值的记录,对可能的值进行填补和不处理三种情况.

1.2 异常值分析

异常值是指样本数据中的个别值,其数据明显偏离其余观测值,异常值分析是检验数据是否有录入错误以及不合常理的错误数据.具体异常值分析方法:

  1. 简单统计量分析
    通过一些统计量来排除不合理数据,譬如年龄的最大值和最小值,排除年龄199岁不合理的数据.
  2. 3σ原则
    如果数据服从正态分布(钟型曲线),在3σ原则下,异常值是与平均值超过3倍标准差(方差开根号)的值,也就是说起出现的概率为|x-u|>3σ为0.003,属于个别的小概率事件,如果不符合正态分布,可以用远离平均值的多少倍来描述.
  3. 箱型图分析
    四分位数就是将一组数据均分为四等分的点,一组数据被分为四等分需要有三个点,分别被称为:第一四分位数(下四分位数)、第二四分位数(中位数)、第三四分位数(上四分位数),四分位距计算温和异常值和极端异常值的范围(将上四分位数用Q3表示、下四分位数用Q1表示、四分位距用IQR表示(Q3-Q1))。温和异常值的范围:上限为Q3+1.5IQR,下限为Q1-1.5IQR,处在这个范围内的值为温和异常值,一般用圆圈表示;极端异常值的范围:上限为Q3+3IQR,下限为Q1-3IQR,处在这个范围内的值为极端异常值,一般用星号表示。箱型图处理异常值结果比较客观,在识别异常值方面具有一定的优越性.
    我们依照数据作为测试,该测试可在码云下载,具体文件路径demo/data/catering_sale.xls,分析餐饮数据可知,部分数据存在缺失,所以我们通过Pandas读入数据,然后通过describe()函数查看数据基本情况.
#-*- coding: utf-8 -*-
#餐饮销量数据统计量分析
from __future__ import print_function
import pandas as pd
catering_sale = '../data/catering_sale.xls' #餐饮数据
data = pd.read_excel(catering_sale, index_col = u'日期') #读取数据,指定“日期”列为索引列
data.describe() #保存基本统计量

获得以下结果:

          销量
count   200.000000 #通过len(data)可以得出一共有201条,而现在缺失一共1条
mean   2755.214700 #平均值
std     751.029772 #标准差
min      22.000000 #最小值
25%    2451.975000 #1/4分位数
50%    2655.850000
75%    3026.125000
max    9106.440000 #最大值

为了更容易显示这些数据,同时来检测异常值,可以使用箱型图,具体代码如图:

#-*- coding: utf-8 -*-
import pandas as pd

catering_sale = '../data/catering_sale.xls' #餐饮数据
data = pd.read_excel(catering_sale, index_col = u'日期') #读取数据,指定“日期”列为索引列

import matplotlib.pyplot as plt #导入图像库
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

plt.figure() #建立图像
p = data.boxplot(return_type='dict') #画箱线图,直接使用DataFrame的方法
x = p['fliers'][0].get_xdata() # 'flies'即为异常值的标签
y = p['fliers'][0].get_ydata()
y.sort() #从小到大排序,该方法直接改变原对象

#用annotate添加注释
#其中有些相近的点,注解会出现重叠,难以看清,需要一些技巧来控制。
#以下参数都是经过调试的,需要具体问题具体调试。
for i in range(len(x)): 
  if i>0:
    plt.annotate(y[i], xy = (x[i],y[i]), xytext=(x[i]+0.05 -0.8/(y[i]-y[i-1]),y[i]))
  else:
    plt.annotate(y[i], xy = (x[i],y[i]), xytext=(x[i]+0.08,y[i]))
plt.show() #展示箱线图

根据箱型图在这里插入图片描述可知,上下界一共有8个异常值.基于业务角度出发,将4065.2,4060.3,865.0归为正常值,最后异常值为5个,缺失值为1个.我们可以设置过滤规则为日销售量在400到5000之间为正常数据.

1.3 一致性分析

数据的不一致性是指数据的矛盾性和不相容性,主要发生在数据集成中.例如不同的表中存储了用户的电话,用户的电话号码发生改变,而只更新一张表的数据,那么这两张表存在不一致的数据.

2. 数据特征分析

对数据进行质量分析后,即可以对数据进行特征分析,得出其规律和趋势.

2.1 分布分析

分布分析可以揭示数据的分布特征和分布类型,对于定量数据,通过频率分布图,频率分布直方图,茎叶图可以得出是对称分布还是非对称分布,对于定性分类数据,可以通过饼图和条形图直观的显示分布情况.

2.1.1 定量数据的分布分析

对于绘制频率分布直方图,选择组数和组宽最为重要,以下是绘制步骤:

  1. 求极差
  2. 决定组距与组数
  3. 决定分点
  4. 列出频率分布表
  5. 绘制频率分布直方图

遵循的规则:

  1. 必须包括所有数据
  2. 各组相互排斥
  3. 各组组宽最好相等

下面结合具体业务场景进行分析,数据详见:demo/data/catering_sale.xls

  1. 求极差:极差=最大值-最小值=4065.2-865=3200.2
  2. 分组:确定组距为600,得到组数 3200/600=5.3=6
  3. 决定分点:起始比最小值稍小,结束值比最大值最大,分布区间如图所示
[800,1400) [1400,2000) [2000,2600)
[2600,3200) [3200,3800) [3800,4400)
  1. 绘制频率分布表
    组段 | 组中值 | 频数 | 频率f | 累计频率
    :-: | :-: | :-: | :-: | :-:
    [800,1400) | 1100 |1 |0.5% |0.5%

5.绘制频率分布直方图,绘图类似如图,此图并不是以上数据对应图,只是类似:在这里插入图片描述

2.1.2 定性数据分析

对于定性变量,常常根据变量分类类型进行分组,可以采用饼图和条形图来描述定性变量的分布.饼图的每一部分代表每一类型的百分比或频数,大小与频数成正比.条形图的高度代表每一类型的百分比或频数.下面是菜品ABC在某段时间的销量占比:在这里插入图片描述

2.2 对比分析

对比分析是把两个相互联系的属性进行比较,从数量上展示研究对象规模大小,水平高低,速度快慢,以及各种关系是否协调.适合指标间的横纵向比较,时间序列的比较分析.对比分析主要包括绝对数比较和相对数比较,绝对数比较是直接进行对比,没有进行处理.相对数比较是由两个有联系的指标对比计算所得,其数值表现为相对数,相对数比较分为以下几种:

  1. 结构相对数:将同一主体内部分数值与全部数值对比求得比重,用以说明事物性质,结果,或数量,例如居民消费占总消费支出比重,产品合格率.
  2. 比例相对数:将同一主体部分数值相互比较,表明各部分的比例关系.如人口性别比例,投资与消费比例.
  3. 比较相对数:将同一时期两个相同指标在不同区域比较,例如不同地区商品价格对比,不同企业同项指标对比.
  4. 强度相对数:将两个不同指标但具有一定联系进行对比,用以说明强度,密度和普遍程度.例如人均国内生产总值"元/人",密度"人/平方公里".
  5. 计划完成相对数:实际完成数与计划数进行对比,用以说明计划完成度.
  6. 动态相对数:将同一指标不同时期进行比较,用以说明发展速度和变化速度,例如发展速度和增长速度.
    如图:在这里插入图片描述在这里插入图片描述

2.3 统计量分析

用统计指标对数据进行统计描述,常从集中趋势和离中趋势进行分析.集中趋势主要由均值和中位数,反映变异程度的指标是离中趋势,主要由标准差和四分位间距.

  1. 集中趋势度量
    均值,均值是所有数据的平均值.算术平均值计算公式:
    在这里插入图片描述,
    为了反映数据集不同成分所占不同重要程度,为每一个x赋予权重,加权均值计算公式为:在这里插入图片描述,
    频率分布表计算公式为:
    在这里插入图片描述,
    其中x为组中值,f为各个组段的频率.
    作为一个统计量,均值对极端值异常敏感,存在极端值,均值就不能很好地度量数据集数据的集中趋势,就必须使用截断均值(去掉最高最低)或中位数.
    中位数,是指数据集数据从小到大排序位于中间的数据.奇数中位数计算公式:
    在这里插入图片描述,
    偶数中位数计算公式:
    在这里插入图片描述
    众数,是数据中出现次数最多的数据,众数适用于定性变量,不具备唯一性,用于离散型变量而非连续型变量.
  2. 离中趋势度量
    极差,极差是最大值与最小值的差,对极端值非常敏感.
    标准差,度量数据偏离均值的程度,计算公式:
    在这里插入图片描述
    变异系数,度量标准差相对于均值的离中趋势,用来比较多个具有不同单位或不同波动幅度的数据集的离中趋势,计算公式为:
    在这里插入图片描述
    四分位数间距,四分位数间距是上四分位数与下四分位数之差,期间包含全部观察值一半,值越大,离中趋势越大.
    下面的代码将对餐饮数据进行统计量分析:
#-*- coding: utf-8 -*-
#餐饮销量数据统计量分析
from __future__ import print_function
import pandas as pd

catering_sale = '../data/catering_sale.xls' #餐饮数据
data = pd.read_excel(catering_sale, index_col = u'日期') #读取数据,指定“日期”列为索引列
data = data[(data[u'销量'] > 400)&(data[u'销量'] < 5000)] #过滤异常数据
statistics = data.describe() #保存基本统计量
statistics.loc['range'] = statistics.loc['max']-statistics.loc['min'] #极差
statistics.loc['var'] = statistics.loc['std']/statistics.loc['mean'] #变异系数
statistics.loc['dis'] = statistics.loc['75%']-statistics.loc['25%'] #四分位数间距
print(statistics)

输出为:

                销量
count   195.000000
mean   2744.595385
std     424.739407
min     865.000000
25%    2460.600000
50%    2655.900000
75%    3023.200000
max    4065.200000
range  3200.200000
var       0.154755
dis     562.600000

2.4 周期性分析

周期性分析是探索某个变量是否随时间变化而呈现某种周期变化趋势.例如要对某单位用电量进行预测,可以单位日用电时序图直观的估计用电量变化趋势:
在这里插入图片描述

2.5 贡献度分析

贡献度分析也叫做帕累托分析,它的原理是2/8定律.例如一个公司80%的利润来源于20%的产品,而其他80%的产品只产生20%的利润.
应用贡献度分析餐饮企业可以重点改善盈利最高的钱80%的菜品或者重点发展综合影响最高的80%的部门.下面是A1到A10菜品某个月盈利额数据详见:demo/data/catering_dish_profit.xls,帕累托分析代码为:

#-*- coding: utf-8 -*-
#菜品盈利数据 帕累托图
from __future__ import print_function
import pandas as pd

#初始化参数
dish_profit = '../data/catering_dish_profit.xls' #餐饮菜品盈利数据
data = pd.read_excel(dish_profit, index_col = u'菜品名')
data = data[u'盈利'].copy()
data.sort_values(ascending = False)

import matplotlib.pyplot as plt #导入图像库
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

plt.figure()
data.plot(kind='bar')
plt.ylabel(u'盈利(元)')
p = 1.0*data.cumsum()/data.sum()
p.plot(color = 'r', secondary_y = True, style = '-o',linewidth = 2)
plt.annotate(format(p[6], '.4%'), xy = (6, p[6]), xytext=(6*0.9, p[6]*0.9), arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) #添加注释,即85%处的标记。这里包括了指定箭头样式。
plt.ylabel(u'盈利(比例)')
plt.show()

生成贡献度分析图:在这里插入图片描述
根据帕累托法则,应重点发展A1-A7,减少对A8-A10菜品的投入.

2.6 相关性分析

分析连续变量之间的线性相关程度并且用适当的统计指标表现出来的过程称为相关性分析.通常有三种方法,包括直接绘制散点图,绘制算点图矩阵,计算相关系数.

  1. 直接绘制散点图
    判断两个变量是否具有线性相关最直接的是绘制散点图:
    在这里插入图片描述
  2. 绘制散点图矩阵
    需要同时考虑多个变量之间的相关关系时需要利用散点图矩阵同时绘制各变量间的散点图,快速发现各变量之间的相关性,这在进行多元线性回归时尤为重要:
    在这里插入图片描述
    为了更精确地描述变量之间的线性相关程度,可以计算相关系数.在二元变量相关性分析过程中比较常用的有Pearson相关系数,Spearman秩相关系数和判定系数.

  1. Pearson相关系数一般用于分析两个连续变量之间的关系,其计算公式如下:
    在这里插入图片描述
  2. Spearman秩相关系数,Pearson相关系数需要满足正态分布,不服从正态分布,分类或等级变量之间的相关性可以使用Spearman秩相关系数.
    秩:可以理解成就是一种顺序或者排序,那么它就是根据原始数据的排序位置进行求解.
    用途:用于解决称名数据和顺序数据相关的问题。适用于两列变量,而且具有等级变量性质具有线性关系的资料.能够很好处理序列中相同值和异常值.
    公式:
    在这里插入图片描述,
    n为等级个数,d为二列成对变量的等级差数.
    计算过程:先对两变量(X, Y)排序,记下排序以后位置(X’, Y’),(X’, Y’)值称为秩次,秩次差值就是上面公式中的di,n就是变量中数据的个数,最后带入公式就可求解结果,如图:
    在这里插入图片描述
    ,相关性系数:ρs= 1-6*(1+1+1+9)/6*35=0.657.
    在实际运算中,需要对两种相关系数进行假设检验,用t检验方法检验其相关程度.正态分布下,两者等价,对于连续数据更适合Pearson相关系数.
  3. 判定系数判定系数是相关系数的平方,用来衡量回归方程对y的解释程度,判定系数在0到1之间,越接近1,表示相关性越强,越接近0,表示几乎没有直线相关关系.餐饮系统不同菜品的日销量数据在demo/data/catering_sale_all.xls,分析菜品之间相关关系,比如替补菜品,互补菜品或者没有关系,为原材料采购提供参考.python代码:
#-*- coding: utf-8 -*-
#餐饮销量数据相关性分析
from __future__ import print_function
import pandas as pd
catering_sale = '../data/catering_sale_all.xls' #餐饮数据,含有其他属性
data = pd.read_excel(catering_sale, index_col = u'日期') #读取数据,指定“日期”列为索引列

data.corr() #相关系数矩阵,即给出了任意两款菜式之间的相关系数
data.corr()[u'百合酱蒸凤爪'] #只显示“百合酱蒸凤爪”与其他菜式的相关系数
data[u'百合酱蒸凤爪'].corr(data[u'翡翠蒸香茜饺']) #计算“百合酱蒸凤爪”与“翡翠蒸香茜饺”的相关系数

只显示“百合酱蒸凤爪”与其他菜式的相关系数

百合酱蒸凤爪     1.000000
翡翠蒸香茜饺     0.009206
金银蒜汁蒸排骨    0.016799
乐膳真味鸡      0.455638
蜜汁焗餐包      0.098085
生炒菜心       0.308496
铁板酸菜豆腐     0.204898
香煎韭菜饺      0.127448
香煎罗卜糕     -0.090276
原汁原味菜心     0.428316
Name: 百合酱蒸凤爪, dtype: float64

根据数据显示,百合酱蒸凤爪与乐膳真味鸡,原汁原味菜心相关系数较高,顾客会同时点这几种菜品.

3.Python主要数据探索函数

Python中用于数据探索的库主要是Pandas(数据分析)和Matplotlib(数据可视化),Pandas提供大量数据探索函数,分类为统计作图函数和统计特征函数,作图函数依赖于Matplotlib,所以双方一般结合使用.

3.1 基本统计特征函数

基本特征函数主要用于计算数据的均值,方差,标准差,分位数,相关系数和协方差,这些方法主要作为Pandas对象DataFrame或Series方法出现.pandas主要统计特征函数:

方法名 函数功能 所属库
sum() 按列计算数据样本总和 Pandas
mean() 计算数据样本算术平均数 Pandas
var() 计算样本数据的方差 Pandas
std() 计算数据样本的标准差 Pandas
corr() 计算数据样本斯皮尔曼(Pearson)相关系数矩阵 Pandas
cov() 计算数据样本的协方差矩阵 Pandas
skew() 样本值的偏度(三阶矩) Pandas
kurt() 样本值的峰度(四阶矩) Pandas
describe() 给出样本基本统计量 Pandas

sum(),mean(),var(),std(),skew(),kurt()使用格式D.方法名,样本D为DataFrame或者Series.
corr()的调用方法为D.corr(method=‘pearson’),样本D为DataFrame,返回相关系数矩阵,method参数为计算方法,支持pearson,kendall,spearman,S1.corr(S2,method=‘pearson’),S1,S2均为Series,这种格式计算两个Series之间的相关系数.
cov()的调用方法D.cov(),样本D为DataFrame,返回协方差矩阵,S1.cov(S2),S1,S2均为Series,这种格式计算两个Series之间的协方差.
describe()的调用方法D.describe(),样本D为DataFrame,返回基本统计量,括号可以带一些参数,比如p = [0.2,0.4,0.6,0.8]是只计算0.2,0.4,0.6,0.8分位数,而不是默认分位数.

3.2 拓展统计特征函数

除了基本的统计特征函数,Pandas还拓展了统计特征函数,主要有累积计算(cum)和滚动计算(pd.rolling),下图为Pandas累积统计特征函数:

方法名 函数功能 所属库
cumsum() 依次给出前1,2…n个数的和 Pandas
cumprod() 依次给出前1,2…n个数的积 Pandas
cummax() 依次给出前1,2…n个数的最大值 Pandas
cummin() 依次给出前1,2…n个数的最小值 Pandas
rolling_sum() 计算数据样本的总和,按列计算 Pandas
rolling_mean() 数据样本的算术平均数 Pandas
rolling_var() 计算数据样本的方差 Pandas
rolling_std() 计算数据样本的标准差 Pandas
rolling_corr() 计算数据样本的相关系数矩阵 Pandas
rolling_cov() 计算数据样本协方差矩阵 Pandas
rolling_skew() 样本值的偏度(二阶矩) Pandas
rolling_kurt() 样本值的峰度(四阶矩) Pandas

其中cum系列函数是以DataFrame和Series的方法出现,所以调用方法是D.cum,而rolling函数是Pandas系列函数,调用格式是pd.rolling_mean(D,k),意思是每k列计算一次均值.

D = pd.Series(range(0.20)) #构造Series,为0-19 20个整数.
D.cumsum() #给出前n项和
pd.rolling_sum(D,2) #依次对相邻两项求和.

3.3 统计作图函数

Python的主要作图库是Matplotlib,Pandas基于Matplotlib并对某些命令做了简化,因此一般二者相互结合使用,Python主要的统计作图函数有:

作图函数名 作图函数功能 所属库
plot() 绘制线性二维图,折线图 Matplotlib/Pandas
pie() 绘制饼形图 Matplotlib/Pandas
hist() 绘制二维条形直方图,可显示数据分配情形 Matplotlib/Pandas
boxplot() 绘制样本数据箱型图 Pandas
plot(logy = True) 绘制Y轴的对数图形 Pandas
plot(yerr = error) 绘制误差条形图 Pandas

作图前需要导入以下代码:

import matplotlib.pyplot as plt #导入作图库
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
plt.figure(figsize = (7,5)) #创建作图区域,指定比例
plt.show() #显示作图结果
  1. plot,Matplotlib的绘图方式:plt.plot(x,y,S’),绘制以x轴为横轴,S’字符串指定绘制图形的类型,样式,颜色等,常用的选项有:'b’为蓝色,'r’为红色,'g’为绿色,‘o’为圆圈,’+‘为加号标记,’-‘为实线,’–'为虚线.
    使用DataFrame或Series对象内置方法作图,D.plot(),默认以index为横坐标,每列数据为纵坐标自动作图,通常使用kind指定作图类型,支持line(线形),bar(条形),barh,hist(直方图),box(箱型图),kde(密度图)
    ,area,pie(饼图)等同时也能够接收plot中的参数,如果数据为Pandas中的对象使用这种方法比较简洁.
    实例,绘制绘制0-2π蓝色正弦星型虚曲线
import numpy as np
x = np.linspace(0,2*np.pi,50) #x坐标输入
y = np.sin(x) #计算x对应y值
plt.plot(x,y,'bp--')# 绘制图形
plt.show() #展示图形
  1. pie,使用Matplotlib的绘图方式绘制饼图,其中size是一个列表,记录各个扇形比例.实例,通过向量[15,30,45,10]绘制饼图,并将第二部分分离出来,代码:
labels = 'Frogs','Hogs','Dogs','Logs' #定义标签
sizes = [15,30,45,10] #每一块比例
colors = ['yellowgreen','gold','lightskyblue','lightcoral'] #定义每一块颜色
explode = [0,0.1,0,0] #仅突出显示第二块
plt.pie(sizes,explode=explode,labels=labels,colors=colors,autopct='%1.1f%%',shadow=True,startangle=90)
plt.axis('equal') #显示为圆
plt.show()

3.hist,使用方法为plt.hist(x,y),其中x为待绘制的一维数组,y可以是整数,表示均分为y组,也可以是列表,列表的各个数字成为各组的分界点,即手动指定分界点.实例,绘制二维直方图,随机生成1000个服从正态分布元素,分成10组绘制直方图:

x = np.random.randn(1000) #1000个服从正态分布随机数
plt.hist(x,10) #分成10组
plt.show()

4.boxplot,使用方法D.boxplot()/D.plot(kind = ‘box’),一种是Dataframe直接调用,另一种为Series或者DataFrame指定plot方法调用,其中盒子的上下四位和中值各有一条线段,箱型末端延伸出去的直线称为须,表示盒外数据长度,如果须外没有数据,则在须的底部有一点,点的颜色和须的颜色相同.实例,绘制两组正态分布随机数样本数据的箱型图,其中一组均值0,标准差为1,另一组均值1,标准差1,代码为:

import matplotlib.pyplot as plt #导入作图库
import numpy as np
import pandas as pd
x = np.random.randn(1000) #1000个服从正态分布随机数
D= pd.DataFrame([x,x+1]).T #构建两列DataFrame
D.plot(kind = 'box') #调用内置方法绘图
plt.show()

5.plot(logx = True)/plot(logy = True),绘制x轴或者y轴的对数图形,对于X轴(Y轴)使用对数刻度(以10为底),Y轴使用线性刻度,进行plot函数绘图,D为Pandas的DataFrame或者Series.实例,指数函数使用plot(logy = True)绘图:

x = pd.Series(np.exp(np.arange(20))) #原始数据
x.plot(label = u'原始数据',legend = True)
plt.show()
x.plot(logy = True,label = u'原始数据',legend = True)
plt.show()

6.plot(yerr=error),D.plot(yerr=error),D为Pandas的DataFrame或者Series,代表均值数据列,而error为误差列,此命令为在y轴画出误差棒图,在x轴画出为xerr=error.实例绘制误差棒图:

error = np.random.randn(10) #定义误差列
print(error)
y = pd.Series(np.sin(np.arange(10))) #均值数据列
y.plot(yerr=error)
plt.show()
版权声明:本文为Rcvisual原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Rcvisual/article/details/100224436