4 anaconda&Numpy&Pandas&Matplotlib

  1. python环境搭建
    1. Jupyter notebook修改初始打开的文件夹路径;在jupyter打开的情况下
      1. cmd->jupyter notebook --generate-config->y
      2. c.NotebookApp.notebook_dir修改jupyter_notebook_config.py的文件夹路径
    2. 启动jupyter notebook出现闪退
      1. 修改配置文件,anaconda prompt输入jupyter notebook再启动jupyter notebook
      2. 修改快捷方式,属性->目标->去除%USERPROFILE%
    3. Anaconda Navigator打不开
      1. 使用管理员运行:conda prompt
      2. 执行命令:conda update anaconda-navigator
      3. 执行anaconda-navigator --reset
      4. 执行conda update anaconda-client
      5. 执行conda update -f anaconda-client
  2. conda
    1. conda: 一个工具,用于包管理和环境管理,其中:包管理与pip类似,管理Python第三方库;将工具、第三方库、Python版本、conda都当作包,同等对待
    2. anaconda:一个集合,包括conda、某版本Python、一批第三方库等
  3. Spyder:python和Ipython的结合,很好的调试程序
    1. Ipython
      1. 变量?:显示通用信息和源代码
      2. %magic:显示所有魔术命令
      3. %run:运行.py程序
      4. 行首In[n]:n为运行次数
  4. jupyter notebook快捷键
    1. 命令模式:按键Esc开启
      1. Ctrl+Enter运行本单元
      2. Shift+Enter运行本单元,选中下一个单元
      3. Alt+Enter运行本单元,在其下方插入新的单元
      4. Shift+k选中上方单元
      5. Shift+j选中下方单元
      6. Shift+m合并选中的单元
      7. 双击D删除选中的单元
    2. 编辑模式:Enter键启动/双击一个单元
      1. Tab代码缩进/代码补全
      2. Shift+tab取消缩进/提示
  1. Numpy
    1. NumPy是一个开源的Python科学计算基础库,包含:
      1. 一个强大的N维数组对象 ndarray,组成部分:
        1. 实际的数据
        2. 描述这些数据的元数据(数据维度、数据类型等)
      2. 广播功能函数
      3. 整合C/C++/Fortran代码的工具
      4. 线性代数、傅里叶变换、随机数生成等功能
      5. NumPy是SciPy、Pandas等数据处理或科学计算库的基础
      6. 数组对象的必要性:
        1. 数组对象可以去掉元素间运算所需的循环,使一维向量更像单个数据;类似R中的矩阵计算
        2. 设置专门的数组对象,经过优化,可以提升这类应用的运算速度
        3. 数组对象采用相同的数据类型,有助于节省运算和存储空间
    2. 数组:数据类型必须相同;二维数组是一维数组的组合形式,以此类推,交叉表即二维数组,表头是二维数组的一部分;多维数组是二维数组在新维度上的扩展;高维数组用字典来表示
      1. 创建numpy数组
        1. numpy.array([[],[]],dtype=);列表中嵌套列表;一个二维数组对象必须整体放在一个[]内
        2. numpy.array(())元祖类型创建
        3. numpy.array([[],()])列表和元祖混合类型创建
        4. numpy.arange(start,end,step)类似range,生成一维数组
        5. numpy.ones((row,col))生成元素为1的二维数组;row,col前面可加dim维度数
        6. numpy.zeros((row,col),dtype=numpy.int32)生成元素为0的二维数组
        7. numpy.eye(n)生成n*n的单位矩阵
        8. numpy.ones/zeros/full_like(a,[val])根据数组a的形状生成一个全是1/0/val的数组
        9. numpy.linspace(start,end,n,endpoint=T)生成包含start和end的n个等距数据的一维数组,endpoint=F则不包含end
        10. numpy.concatenate((a,b))将a,b连接为一个数组
      2. 属性
        1. a.ndim秩,维度数量
        2. a.shape行列数的元祖
        3. a.size元素的个数,行*列
        4. a.dtype元素的类型
        5. a.itemsize每个元素的大小,以字节为单位
      3. 元素类型
        1. bool:T/F
        2. int32:32位长度的整数
        3. uint32:32位长度的无符号整数,从0开始
        4. float32:1位符号位,8位指数
        5. complex64:实部和虚部都是32位浮点数
      4. *非同质的ndarray对象,即数组中的行元素数量不等,会将每行看做对象类型
      5. 数组变换方法
        1. a.reshape(shape)不改变数组元素,返回shape形状的数组,原数组不变,元素的数量不变
        2. a.resize(shape)类似reshape,但修改原数组且不返回新的数组,元素的数量不变
        3. a.swapaxes(1,0)将n维数组中的两个维度进行调换,注意只有两个参数,二维数组的维度没有指明则为0和1
        4. a.flatten()将数组转换为一维数组,降维,原数组不变
        5. a.astype(numpy.float)数组元素的数据类型转化,一定创建一个新的数组,即使两个类型一样,即原数组不变;深拷贝
        6. a.tolist()转化为列表,原数组不变
      6. 操作
        1. 索引a[],从0开始
        2. 切片a[start:end:step]不含end
        3. 多维数组索引a[x,y,z]索引都从0开始
        4. 多维数组的切片a[:,1:3,::2]每个维度的切片和一维数组切片相同
      7. 运算
        1. 标量运算
          1. a.mean()均值
        2. NumPy一元函数:对ndarray中的数据执行元素级运算的函数,都有返回值
          1. numpy.abs/fabs(a)数组各元素的绝对值/float类型转化
          2. numpy.sqrt(a)数组各元素的平方根
          3. numpy.square(a)数组各元素的平方
          4. numpy.log/log10/log2(a)数组各元素的自然对数/10为底对数/2为底对数
          5. numpy.ceil/floor(a)数组各元素的ceiling/floor值,往大/往小取整
          6. numpy.rint(a)数组各元素的四舍五入
          7. numpy.modf(a)数组各元素的小数和整数部分以两个独立数组组合为一个元祖的形式返回
          8. numpy.cos(a)数组各元素的三角函数
          9. numpy.exp(a)以数组各元素为幂的指数值
          10. numpy.sign(a)数组各元素的符号值(1,0,-1)
          11. numpy.copysign(a,b)将b的符号赋给a中对应的元素
        3. NumPy二元函数:两个数组对象的计算
          1. +-*/**两个数组各元素进行对应运算,类似R的矩阵运算
          2. numpy.maximum/minimum(a,b)a和b数组相对应元素的大小比较,返回数组形状相同的对象
          3. numpy.mod(a,b)a和b数组相对应元素的模运算
          4. numpy.copysign(a,b)将b中各元素的符号赋值给a的对应元素
          5. ><==!=a和b数组相对应元素的算术比较
  2. Numpy数据存取与函数
    1. 数据的CSV文件存取:CSV只能有效存储一维和二维数组
      1. numpy.savetxt('file', array, fmt='%.18e', delimiter=',')
        1. fmt写入文件的格式,%d,%.8f,%.18e
        2. delimiter分隔符,默认为空格
      2. numpy.loadtxt('file', dtype=numpy.float, delimiter=',' , unpack=False)
        1. dtype读入数据类型
        2. unpack=T读入的属性将分别写入不同变量
    2. 多维数据的存取
      1. a.tofile('file.dat',format='%d',sep=',')
      2. numpy.fromfile('file', dtype=numpy.float, count=‐1, sep='')
        1. count读入元素个数,‐1表示读入整个文件
        2. 读取的是一维数组
      3. numpy.save('file.npy',array)
      4. numpy.load('file')保存数组结构
    3. 查看当前路径
      1. import os->os.getcwd()
    4. 随机函数
      1. numpy.random.rand(shape)生成[0,1)范围浮点数,均匀分布
      2. numpy.random.randn(shape)生成浮点数,符合标准正态分布
      3. numpy.random.seed(seed)设定随机种子
      4. numpy.random.randint(min,max,(shape))根据min和max确定整数的范围,shape确定数组结构
      5. numpy.random.shuffle(array)将序列的所有元素随机排序,即数组的行顺序变化,改变原数组
      6. numpy.random.choice(a[,size,replce,p]) 从数组a中以概率P来抽取元素,形成shape形状的新数组
      7. numpy.random.permutation(array)效果同shuffle,不改变原数组
      8. numpy.random.uniform(min,max,(shape))根据min和max确定的范围,shape确定数组结构,生成均匀分布数组
      9. numpy.random.normal(mean,scale,(shape))根据mean和scale,shape确定数组结构,生成正态分布数组
      10. numpy.random.poisson(lambda,(shape))根据lambda,shape确定数组结构,生成泊松分布数组
    5. 统计函数;axis=0|1代表按列|行计算
      1. numpy.sum(array)
      2. numpy.mean(array,axis=None)根据axis指定的轴计算期望
      3. numpy.average(array,axis=None,weights=None)加权均值,按列计算则权值对应行,按行计算则权值对应列
      4. numpy.std(array)标准差
      5. numpy.var(array)方差
      6. numpy.argmin(array)降维后的最小值下标值
      7. numpy.argmax(array)降维后的最大值下标值
      8. numpy.unravel_index(index,a.shape)根据a中元素的序列找到该元素的数组位置;重新塑造成多维下标
      9. numpy.ptp(array)计算最大值和最小值的差
      10. numpy.median(array)计算数组的中值
      11. numpy.gradient(array)计算数组a中元素的梯度
        1. 一维数组即元素的(右侧元素-左侧元素)/2,只有一侧时则右侧元素-左侧元素;
        2. 二维数组即按行|列(下-上)的方向上进行计算,返回两个数组
  1. Pandas:提供高性能易用数据类型和分析工具;pandas包含索引的数据对象;http://pandas.pydata.org/
    1. Series:自定义索引值列表
      1. 创建:Series类型的数据可以由python列表,标量,python字典,ndarray,其他函数创建
        1. pandas.Series([10,9,8],index=['a','b','c'])列表创建一组数据
        2. pandas.Series(26,index=['a','b','c','d'])标量值创建一组数据
        3. pandas.Series(dict,index=['c1','d','a','b'])字典创建一组数据,默认键作为索引;有index则表示选择返回的键值对的键
        4. pandas.Series(list,index=numpy.arange)通过numpy创建索引
      2. 属性
        1. s.index
        2. s.values
        3. s.name一般是添加Series对象名
        4. s.index.name一般是添加index的列名
        5. s[index_value]|s[1]索引值/默认索引位置查询数据
        6. s[[index_value_1,index_value_2,...]]多值查询,需要嵌套;索引值和默认索引是并存的,但不能混用
        7. s[start:end:step]左闭右开
      3. 方法
        1. s.median()数值中位数
        2. numpy.exp(s)列表计算
        3. s.get(index,return)判断Series中是否包含index,存在则返回值,不存在则return
        4. index in s判断索引是否在Series中,不能用于值是否存在的判断
        5. s1+s2返回所有的索引,但只返回相同索引的相加值;对齐操作:在运算中会对齐相同索引
      4. 修改:随时修改,即刻生效
        1. s[index_value]|s[1]|s[[index_value_1,index_value_2,...]]=修改值
    2. DataFrame:二维index数据结构;每列的数据类型可以不同,有行索引和列索引,可以表达多维数据;当axis=0时,针对行索引处理每一列数据;当axis=1,针对列索引处理每一行数据
      1. 创建:DataFrame类型的数据可以由二维ndarray对象,一维ndarray对象,列表,字典,元祖,Series作为值而构成的字典,其他DataFrame类型的对象创建
        1. pandas.DataFrame(numpy.array)ndarray对象创建
        2. pandas.DataFrame({col_1_name:pandas.Series(),col_2_name:pandas.Series()})Series来创建
        3. pandas.DataFrame({col_1_name:list1,col_2_name:list2},index)列表创建
        4. pandas.DataFrame({col_1_name:tuple1,col_2_name:tuple2},index)元祖创建
        5. pandas.DataFrame({col_1_name:{dict1},col_2_name:{dict2}})字典创建
      2. pandas数据存取https://blog.csdn.net/u010801439/article/details/80033341;numpy的创建方法将定义每个元素的类型,pandas将每列看做一个object,DataFrame的元素类型可以不同
        1. pandas.DataFrame(data).to_csv('file',index=T|F,header=T|F),index行标签,header列标签
        2. pandas.read_csv('file',encoding='gbk')
      3. DataFrame属性
        1. df.index:index对象
        2. df.columns
        3. df.values具体数据,二维列表
        4. df[col_name]列索引,看做对象,类型为元素的类型
        5. df.loc[row_index]行索引,看做对象
        6. df[col_name][start:end:step]切片,标准格式,列名不能改成默认数值索引,否则只作第一个[]的行切片
  2. Pandas操作
    1. df.reindex(index=[],columns=[])重新自定义行列索引排序
    2. df.reindex(columns=df.columns.insert(col_index,col_name),fill_value=)新增列;列的默认数值索引从0开始
    3. df.reindex(index=df.index.insert(row_index,row_name),method='ffill')新增行;method=ffill|bfill向前|向后填充
  3. Pandas索引index类型的常用方法:index对象是不可变对象
    1. index.append(new_index)结合两个index生成新的index对象
    2. index.diff(new_index)计算差集,生成新的index对象
    3. index.intersection(new_index)计算交集,生成新的index对象
    4. index.union(new_index)计算并集,生成新的index对象
    5. index.delete(loc)删除默认数值索引位置loc的元素
    6. pandas.s|pandas.df.drop([],axis=1)删除指定索引位置的Series|DataFrame;DataFrame删除列必须加上axis=1
  4. Pandas的数据类型运算
    1. +-*/同维数组计算,根据行列索引自动补齐运算,即两个对象的交集,缺失项补NaN;缺失项和两个对象的位置无关
    2. 不同维度数组比如二维减去一维或者0维,这样的运算我们叫广播运算,默认将高维对象的每行(或每个元素)与低维对象进行计算;和高低维对象的位置无关
    3. df1.add(df2,fill_value=)df1+df2,且两个对象在计算中的缺失位置用fill_value代替;即元素较少的对象会将空缺位置的元素用fill_value补齐
    4. df1.sub(df2,fill_value=)df1-df2,且两个对象在计算中的缺失位置用fill_value代替
    5. df1.mul(df2,fill_value=)df1*df2,且两个对象在计算中的缺失位置用fill_value代替
    6. df1.div(df2,fill_value=)df1/df2,且两个对象在计算中的缺失位置用fill_value代替
    7. 不同维度的对象在调用方法时必须是:高维对象.add(低维对象,axis=0|1);axis=0则在列上计算
    8. 比较运算
      1. 相同维度:只能比较相同数据结构的对象,输出的元素是相同位置元素的比较
      2. 不同维度:广播运算,二维和一维计算时,一维列数>二维列数则用0作为二维数据的新列,直到两个对象列数相同
  5. Pandas数据特征;s|df即Series或DataFrame;s,df为不可变对象
    1. 排序
      1. df.sort_index(axis=0|1,ascending=T|F)按行|列索引升序|降序排序
      2. s|df.sort_value(by,axis=0|1,ascending=T|F)根据某一列|行的数据进行排序;by:axis轴上的某个索引或索引列表且Series只能用axis=0;NaN统一放在排序末尾
        1. df.sort_value(by,axis=0|1,ascending=T|F),当by=行索引时,axis必须为1
    2. 统计
      1. s|df.sum()计算指定轴的总和,按axis=0计算,在行索引的基础之上计算每一列值的和
      2. s|df.count()非NaN值的数量,有axis
      3. s|df.mean|median()计算数据的算术平均值|算术中位数,有axis
      4. s|df.var|std()方差|标准差,有axis
      5. s|df.min|max()计算最小|最大值,有axis
      6. s|df.describe()按axis=0的统计汇总,s返回的是Series且属性为列索引;df返回的是DataFrame,用df.describe().loc[]来查看每一列的属性
      7. s|df.rolling(w).function()依次计算相邻w个元素的和sum(),方差var(),算术平均值mean();rolling(w)即某一列中元素和上面的w个元素的计算
    3. 累计统计
      1. s|df.cumsum()前1,2...,n的和,累加,有axis
      2. s|df.cumprob()前1,2...,n的积,累乘,有axis
      3. s|df.cummax|cummin()前1,2...,n的最大|最小值,有axis
    4. 相关分析,协方差矩阵和相关系数矩阵都为对称方阵,即cij元素即i和j列数据的协方差
      1. s1.cov(s2)|df.cov()计算协方差矩阵
      2. s1.corr(s2)|df.corr()计算相关系数矩阵
  1. Matplotlib:可视化;matplotlib.pyplot是绘制各类可视化图形的命令子库,相当于快捷方式,简写为plt;最后都用plt.show()展示图像:定位->作图->细节->savefig()->plt.show()
    1. plt.plot(s)参数为y轴,索引自动生成x轴;
      1. plt.plot(s1,s2)两个以上的列表则s1为x轴,s2为y轴
      2. format_string='r--x'设置连接点的线条颜色和形式;可省略属性名
        1. 颜色字符
          1. b,g:蓝色,绿色等
          2. #008000:RGB颜色
          3. 0.8:灰度值字符串
        2. 曲线形式
          1. -:实线
          2. --:破折线
          3. -.:点划线
          4. ::虚线
          5. ''或' ':无线条
        3. 点形状
          1. .:点标记
          2. ,:像素标记
          3. o:实心圈标记
          4. 1:下花三角标记
          5. 2:上花三角标记
          6. *:星型标记
          7. h:竖六边形标记
      3. 绘制多条曲线时,不能将各曲线的x省略
      4. 不可省略属性名,应用于所有线条和点,优先使用属性名确定的属性
        1. color="green"控制整体颜色,写全
        2. linestyle="dashed"线条风格
        3. marker="o"点形状
        4. markerfacecolor="blue"点颜色
        5. markersize=20点尺寸
      5. *help(plt.plot):https://blog.csdn.net/henni_719/article/details/77370434
    2. plt.ylabel|xlabel|title("")添加y轴|x轴标题|标题
    3. plt.axis([xaxis_start,xaxis_end,yaxis_start,yaxis_end])设置坐标轴的起始和结束
    4. plt.savefig("",dpi=)输出图像存储为文件,默认png格式,dpi修改输出质量
    5. plt.subplot(nrows,ncols,plot_number)将画布分区域并定位到一个子区域;去除,分隔也行;最好将定位赋值给一个对象,在该对象内进行作图
      1. 如果想要不均匀作图,可以将较大的区域先划分出来,再对剩下的区域继续划分
  2. pyplot的中文显示:pyplot并不默认支持中文显示
    1. matplotlib.rcParams['font.family']='SimHei':rcParams修改字体;rcParams对于matplotlib包是全局的
      1. 'font.family'字体
        1. SimHei中文黑体
        2. Kaiti楷体
        3. LiSu隶书
        4. FangSong仿宋
        5. YouYuan幼圆
        6. STSong华文宋体
      2. 'font.style'字体风格,normal,italic
      3. 'font.size'字体大小,large,x-small
      4. matplotlib.rcParams['axes.unicode_minus']=False(中文环境下)正常显示负号
    2. 在有中文输出的地方,增加一个属性:fontproperties
      1. plt.xlabel("",fontproperties="",fontsize=)
        1. fontproperties字体,即'font.family'
        2. fontsize即'font.size'
  3. pyplot的文本显示
    1. plt.text(x,y)以坐标轴的(x,y)位置为起点添加文本
      1. r'$\mu$':r即取消转义字符\的作用,字面意思识别整体字符串;$$包住数学公式;\mu即数学u,\y即数学式y,\pi即π,数学字符可能需要在后面加空格' '
    2. plt.grid(True)给坐标系添加网格线
    3. plt.annotate(s, xy=arrow_crd, xytext=text_crd, arrowprops=dict)
      1. s即string要添加的字符串
      2. xy即箭头尖的指向点,箭头尾自动放置
      3. xytext即文本的起点
      4. arrowprops=dict(facecolor='',shrink=0.1,width=2),属性字典
        1. facecolor即箭头颜色
        2. shrink即箭头尾和文字的间隔
        3. width即箭头最宽的宽度
  4. pyplot的子绘图区域
    1. plt.subplot2grid(GridSpec, CurSpec, colspan=1, rowspan=1)
      1. GridSpec:(3,3)画布的区域划分
      2. CurSpec:(1,0)区域起始位置,索引值从0开始
      3. colspan占据多少列(往右)
      4. rowspan占据多少行(往下)
      5. 一般操作:ax1=plt.subplot2grid();ax1.plot|set_xlabel|set_title|annotate()->plt.show();set_:设置子区域的...
    2. matplotlib.rcParams['figure.figsize']=(5,5)在作图之前设置好整体图像区域的实际尺寸
    3. matplotlib.rc('lines',linewidth=4,color='g')rc方法一次设置多个参数值
    4. plt.tight_layout()在所有子图像作完后调整每个子区域间的间距
  5. *matplotlib.gridspec.GridSpec(3,3)=gs将整个窗口划分为3*3 -> plt.subplot(gs[0,0])可用gs代替划分区域,且可以切片来操作不同大小的区域
    1. gs[1,:-1]这个图占据第二行,除了最后一列的所有列(:不包含结束,单个-1就包含)
    2. gs[1:,-1]这个图占据最有一列,占据第1行以下所有行
  1. *绘图的步骤
    1. 确定绘图范围matplotlib.rcParams['figure.figsize']=(6,6)
    2. 确定绘图具体区域ax1=matplotlib.pyplot.subplot(111)
    3. 整理绘图数据np.array([])数据序列,可以是列表list
      1. x和y的序列
    4. 具体图像函数属性设置
    5. plt.legend()->plt.show()
  2. Matplotlib绘图函数:import matplotlib.pyplot as plt
    1. 坐标图plt.plot(x,y,format_string)
    2. 箱线图plt.boxplot(data,notch,position)
    3. 条形图plt.bar(left,height,width,bottom)
      1. x|left:标量序列,表示的是条形图x坐标,在x为离散型的条形图中(x离散为条形图,x为连续为直方图),一般为range(len(x))
      2. height:标量或者是标量序列,条形图的高度
      3. width:标量或者数组,可选参数(可以省略),默认宽度是0.8
      4. bottom:标量或者数组,可选参数,条形图的y坐标
      5. x为离散变量即条形图:plt.bar(range(len(x)),data,width,align,tick_label=x,color='rgb')
        1. range(len(x))输出x长度的序列0:len(x)
        2. tick_label设置x轴标签
        3. facecolor='g',color='rgb':前者将条形图统一成一种颜色,后者可以使用缩写颜色字符设置每个条形图颜色不一样
    4. 堆叠柱状图plt.bar(range(len(x)),y1,label,facecolor,align),plt.bar(range(len(x)),y2,bottom=y1,label,facecolor,tick_label,align)label即图例元素,bottom选择堆叠图像y1数据在y2下面
      1. plt.legend()显示图例
    5. 并列柱状图plt.barh(bottom=(0,1,2),width=(25,35,20),height=0.1,color='rgb')
    6. 横向条形图plt.barh(width,bottom,left,height)
      1. bottom条形图y坐标
      2. width宽度
      3. height高度,起始是条形图的柱状宽度
    7. 极坐标图plt.polar(theta,r)
      1. theta=numpy.linspace(0.0, 2 * np.pi, N, endpoint=False)生成角度的等差数列且不包含2 * np.pi
      2. radii=10 *np.random.rand(N)生成0~10的随机数,即半径
      3. width=np.pi / 4 * np.random.rand(N)生成随机数对应跨越的角度,从0度开始;在设定随机种子的前提下两个随机是相同的
      4. plt.subplot(111,projection='polar').bar(theta,radii,width=width)在设定图形位置时,设置为极坐标图,且对应的角度,随机数和宽度
    8. 饼图plt.pie(sizes,explode,labels=(0,0.2,0.1,0),autopct='%.2f%%',shadow=False,startangle=90)
      1. sizes:每个饼状的大小(数值|百分比)
      2. explode:与原图相比的突出距离
      3. labels:每块饼状的标记或者名称
      4. autopct:数学百分比显示格式,两个%是转义字符
      5. shadow:表示扇形是否有阴影
      6. startangle=90:起始角度,即饼图中的某条分隔线在y或x轴上,形成90度角
      7. plt.pie之后plt.axis('equal')表示坐标轴相等,展示圆形饼图而不是椭圆形
    9. 散点图plt.scatter(x,y,C,marker)
      1. x数据的横坐标
      2. y数据的纵坐标
      3. C颜色
      4. marker点的样式
    10. 直方图,x为连续数据,一般绘制正态分布或其他分布函数图像:plt.hist(a,bins,normal,histtype,facecolor,alpha)
      1. a:需要绘制的数据,x坐标
      2. bins:直方条个数
      3. density:当为1时表示数据出现的频率,当为0时表示数据出现的频数
      4. histtype:条形的类别,默认的是bar
      5. facecolor:直方条的颜色
      6. alpha:直方条透明度(值越小,越来越透明)

猜你喜欢

转载自blog.csdn.net/u013103305/article/details/83338670