葫芦书学习笔记

1.为什么需要对数值类型的特征做归一化处理?

  • 为了方便后续进行梯度下降的时候加速收敛
  • 归一化通常主要分为两种:min-max(线性函数归一化),Z-Score(零均值归一化)
  • 需要进行归一化的模型:线性回归,LR,SVM,神经网络等
  • 决策树模型不适用归一化处理,因为决策树在进行节点分裂时主要依据数据集D关于特征x的信息增益比,而信息增益比和特征是否进行归一化无关,因为归一化并不改变样本在特征x上的信息增益。

2.􏳉􏴔􏳓在数据进行预处理时,应该怎样处理类别型特征?

  • 类别型特征原始输入形式通常是字符串形式、除了决策树等少量模型能直接处理字符串形式的输入,对于LR、SVM等模型来说,类别型特征必须经过处理转换成数值型特征才能正确工作
  • 序号编码(ordinal):类别间具有大小关系
  • 独热编码(one-hot):类别间没有大小关系,特征的每个值作为一列。维度过高可能导致维度灾难,产生过拟合问题。
  • 二进制编码:先用序号编码给每个类别赋予一个类别ID,然后将ID转为二进制编码作为结果。

3.什么是组合特征?如何处理高维组合特征(解决高维组合特征维度过高的问题)?

  • 可以将M * N矩阵分解为M * K和K * N两个矩阵相乘的形式,这样参数从M * N降到 K * (M+N)
  • 为了提高复杂关系的拟合能力,在特征工程中经常会把一阶离散特征两两组合,构成高阶组合特征。

#4.怎样有效的找到组合特征?

  • 基于决策树的特征组合寻找。(原始输入构建决策树可以采用梯度提升决策树即每次都在之前构建的决策树的残差上构建下一课决策树)

5.有哪些文本模型?他们各有什么优缺点?

  • 词袋模型(bag of words):最基础的文本表示模型是词袋模型。顾名思义,就是将每篇文章看成一袋子词,并忽略每个词出现的顺序。将整段文本以词为单位切分开,然后每篇文章可以表示成一个长向量,向量中的每一维代表一个单词,而该维对应得权重则则反映了这个词在原文章的重要程度。但是词袋忽略了由几个词组成一个意思这种情况(“如NBA吐槽大会”这种,分解成了NBA和吐槽大会,结果匹配了很多李诞这样和NBA完全不相关的物料)
  • N-gram模型:词袋模型的改进,N-gram将连续出现的N个词组成的词组也作为一维放到向量表示中去。但是N-gram不能识别两个不同的词有相同的主题
  • TF-TDF:TF-IDF(t,d) = TF(t,d)*IDF(t)其中,TF(t,d)为单词t在文档d中出现的频率,IDF(t) = log(文章总数/(包含单词t的文章总数+1)) ,IDF公式可理解为如果一个词出现的文章数越多那么说明它越是一个通用词,通用词对文档内容贡献度比较小
  • 主题模型:主题模型用于从文本库发现有代表性的主题(得到每个主题上面词的分布特性),并且能够计算出每篇文章的主题分布。
  • 词嵌入与深度学习模型:词嵌入是一类将词向量化的模型的统称,核心思想是将每个词都映射成低维空间(通常50-300维)上的一个稠密向量。K维空间中的每一维都可以看作是一个隐含的主题,只不过不像主题模型中的主题那么直观。由于词嵌入将每个词映射成一个K维的向量,如果一篇文章有N个词,就可以用一个N*K维的矩阵来表示这篇文档,但是这样表示过去底层。在实际应用中,如果仅仅把这个矩阵作为源文本的表示特征输入到机器学习模型中,通常很难得到满意的结果。因此,还需要在此基础上加工出更高层的特征。在传统的浅层机器学习模型中,一个好的特征工程往往可以带来算法效果的显著提示。深度学习模型正好为我们提供了一种自动 的进行特征工程的方式,模型中的每个隐层都可以认为对应着不同抽象层次的特征。卷积神经网络和循环神经网络的结构在文本表示中取得很好的效果,主要是由于他们能够更好的对文本进行建模,抽取出更高层的语义特征。。与全链接网络结构相比,卷积神经网络和RNN一方面很好的抓住了文本的特征,另一方面又减少了网络学习中待学习的参数,提高了训练速度,并且降低了过拟合的风险。

6.Word2Vec是如何工作的?它和LDA有什么区别与联系?

  • word2vec实际上一种浅层的神经网络模型,它有两种网络结构,分别是CBOW(continues bag of words)和Skip-gram
  • CBOW的目标是根据上下文出现的词语来预测当前词的生成概率;skip-gram是根据当前词来预测上下文中各词的生成概率。
  • word2vec是google开发的一种词向量嵌入的模型,主要分为CBOW和skip-gram两种,最后得到得词向量是dense vector。
  • LDA是一种生成模型,最后可以得到文档与主题,主题与词之间的概率分布。

7.在图像分类任务中,训练数据不足会带来什么问题?如何缓解数据量不足带来的问题?

  • 训练数据不足主要表现在过拟合方面。
  • 两类处理方法:一是基于模型的方法,主要是采用降低过拟合风险的措施包括简化模型(非线性简化为线性)、添加约束项以缩小假设空间、集成学习、Dropout超参数等。二是基于数据的的方法,主要是通过数据扩充

8.准确率的局限性

  • 不同类别的样本比例非常不均匀时,占比大的类别往往成为影响准确率的最主要因素

9.精确率与召回率的权衡

  • 只用某个点对应的精确率和召回率不能全面地衡量模型的性能,只有通过P-R曲线的整体表现,才能够对模型进行更为全面的评估

10.平方根误差的意外

  • 一般情况下,RMSE能够很好的反映回归模型预测值与真实值的偏离程度。但在实际问题中,如果存在个别偏离程度非常大的离群点时,即使离群点数量非常少,也会让RMSE指标变得很差。
  • 解决方法:一,在数据预处理时过滤这些噪声点。二,如果不认为这些离群点是噪声的话就要进一步提高模型的预测能力,将离群点产生的机制建模进去。三,找一个更合适的指标来评估该模型。

11.什么是ROC曲线

  • ROC􏵻􏵲􏰋曲线是Receiver Operating Characteristic Curve􏰊􏵱􏲒􏰢􏱟的简称,中文名为“受试者工作特征曲线”。ROC曲线的横坐标为假阳性率FPR;纵坐标为真阳性率TPR。

#12.如果绘制ROC􏵻􏵲􏰋曲线

  • ROC􏵻􏵲􏰋曲线是通过不断移动分类器的“截断点”来生成曲线上一组关键点的。
  • 首先根据样本标签统计出正负样本的数量,假设正样本数量为p,负样本数量为n;接下来,把横轴的刻度间隔设置为1/n,纵轴的刻度间隔设置为1/p;再根据模型输出的预测概率对样本进行排序依次遍历样本,同时从零点开始绘制ROC􏵻􏵲􏰋曲线,每遇到一个正样本就沿纵轴方向绘制一个刻度间隔的曲线,每遇到一个负样本就沿着横轴方向绘制一个刻度间隔的曲线,直到遍历完所有样本,曲线最终停在(1,1)这个点,整个ROC曲线绘制完成。

13.如何计算AUC

  • 沿着ROC横轴做积分。

14.ROC曲线相比P-R曲线有什么特点

  • 当正负样本的分布发生变化时,ROC曲线的形状能够基本保持不变,而P-R曲线的形状一般会发生较剧烈变化。
  • ROC曲线能够尽量降低不同测试集带来的干扰,更加客观地衡量模型本身的性能。ROC曲线的适用范围更广,适用于排序、推荐、广告。选择ROC曲线还是P-R曲线因实际问题而异,如果希望更多的看到模型在特定数据集上的表现,P-R曲线能够更直观地反映其性能。

15.结合你的学习和研究经历,探讨为什么在一些场景中要使用余弦相似度而不是欧氏距离

  • 当一对文本相似度的长度差距很大、但内容相近时,如果采用词频或词向量作为特征,它们在特征空间中的欧式距离通常很大;而余弦相似度,它们之间的夹角可能很小,因而相似度更高。此外,在文本、图像、视频等领域,研究的对象的特征维度往往很高,余弦相似度在高维情况下依然保持“ 相同为1,正交是为0,相反时为-1”的性质,而欧式距离的数值则受维度的影响,范围不固定,并且含义也比较模糊。
  • 在一些场景中,例如Word2Vec中,其向量的模长是经过归一化的,此时欧式距离与余弦距离有着单调的关系。此场景下余弦相似度和欧式距离的结果是相同的
  • 欧式距离体现数值上的绝对差异,余弦距离体现方向上的相对差异。分析两个不同用户对于不同视频的偏好,更关注相对差异,显然应当用余弦距离。分析用户活跃度,以登陆次数和平均观看时长作为特征,余弦距离为认为(1,10)􏱤(10,100)两个用户距离很近;但显然这两个用户活跃度是有着极大的差异的,此时更关注数值绝对差异,应当使用欧式距离。

数学基础知识整理

##线性代数

线性相关与线性无关

  • avatar

线性相关的判定:根据观察,利用定义即可判断。

线性相关的判断定理:

  • avatar

  • avatar

矩阵的秩

一个向量组A的秩是A的线性无关的向量的个数

如果把一个向量组看成一个矩阵,则向量组的秩就是矩阵的秩

  • avatar

  • avatar

向量的范数

  • avatar

####常用的向量范数

1-范数 $||x||1=\sum{i=1}^{n}|x|$

2-范数 $||x||2=\sqrt{\sum{i=1}^{n}{x_i}^2}$欧式范数

无穷范数 $||x||_n=max|x_i|$

矩阵的范数

avatar

####常用的矩阵范数

avatar

avatar

范数的作用:机器学习的分类问题中,使用范数可以判断两个特征向量和矩阵的相似性

####矩阵的迹

avatar

线性变换及其矩阵表示

avatar

特征值、特征向量

avatar

####特征值的性质

avatar

####特征值和特征向量的求法

avatar

avatar

特征值和特征向量在机器学习中的应用:• 主成分分析• 流行学习• LDA

正交投影

在线性代数和泛函分析中,投影是从向量空间映射到自身的一种线性变换。具体来说,正交投影是指像空间U和零空间W相互正交子空间的投影

从解方程角度看,A x = b 可能无解,因为对任意 的 x , Ax 总是在A的列子空间里,若 向量 b 不在 列空间里,则方程无解。但是我们可以将 b 利用正 交投影矩阵投影到 A 的列子空间里得到正交投影 y, 然后求解A x = y,寻找一个最佳近似解 x。

avatar

二次型

avatar

avatar

二次型补充知识点

avatar

矩阵的QR分解

avatar

SVD奇异值分解

avatar

##微积分

集合的定义

avatar

####集合的表示方法

avatar

####集合的分类

avatar

集合运算

avatar

Venn图

表示集合的另一种形式

avatar

函数定义

avatar

领域的定义

avatar

####函数的极限性质

四则运算

avatar

#####复合函数的极限

avatar

#####保号性

avatar

#####夹逼定理

avatar

#####洛必达法则

avatar

####函数的连续性

avatar

####间断的定义

avatar

avatar

函数的导数

avatar

####导数的常用公式

avatar

导数的性质

#####四则运算

avatar

#####复合函数求导

avatar

导数作用

链式求导法则:神经网络反向传播基础

梯度下降法:最简单的优化方法

函数的微分

avatar

原函数

avatar

不定积分

avatar

avatar

avatar

####不定积分性质

avatar

####不定积分的基本公式

avatar

定积分

avatar

avatar

avatar

avatar

avatar

####定积分的性质

avatar

牛顿-莱布尼兹公式

avatar

avatar

####二重积分

avatar

avatar

avatar

avatar

####导数

avatar

#####标量关于标量X的求导

avatar

#####向量关于标量X的求导

avatar

####矩阵关于标量X的求导

avatar

####标量关于向量x的导数

avatar

####向量关于向量x的导数

avatar

####矩阵关于向量 x 的导数

avatar

####标量关于矩阵的导数

avatar

####向量关于矩阵的导数

avatar

矩阵关于矩阵的导数

avatar

####分子布局法与分母局部法区别

avatar

####Hessian矩阵

avatar

avatar

##概率论基础

####概率论基础

概率论与数理统计是研究什么的?

1
2
3
随机现象:不确定性与统计规律性 
概率论:从数量上研究随机现象的统计规律性的科学
数理统计:从应用角度研究处理随机性数据,建立有效的统计方法,进行统计推理

随机试验

在概率论中,将具有下述三个特点的试验称为随机试验,简称试验。 随机试验常用E表示

1
2
3
1.试验的可重复性 —— 在相同条件下可重复进行;
2.一次试验结果的随机性 —— 一次试验的可能结果不止一个,且试验之前无法确定具体是哪种结果出现;
3.全部试验结果的可知性 —— 所有可能的结果是预先可知的,且每次试验有且仅有一个结果出现。

####样本空间与样本点

avatar

####随机事件

avatar

####事件的性质与运算

事件的本质是集合,集合的一切性质和运算都适用与事件

####频率与概率

avatar

概率的性质

avatar

古典概型

avatar

avatar

几何概型

avatar

avatar

条件概率

avatar

avatar

####条件概率的几何意义

avatar

####加法公式

avatar

####乘法公式

avatar

####排列组合

avatar

####全概率公式

avatar

####离散分布 vs 连续分布

avatar

####伯努利分布

avatar

####二项分布

avatar

####期望

avatar

期望的性质
1
2
3
4
5

1、E (C ) = C
2、E (aX ) = a E (X )
3、E (X + Y ) = E (X ) + E (Y )
4、当X ,Y 相互独立时,E (X Y ) = E (X )E (Y )
期望的数学含义

反应了数据的平均取值情况

####方差

avatar

avatar

####数据归一化

avatar

####高斯分布

avatar

avatar

####分布函数

avatar

avatar

####均匀分布

avatar

avatar

####指数分布

avatar

avatar

####二维随机变量

avatar

####联合分布函数

avatar

avatar

####联合分布列

avatar

####二维连续型随机变量及其密度函数

avatar

####联合密度性质

avatar

avatar

avatar

avatar

####边缘分布

avatar

avatar

avatar

avatar

####多维分布

在机器学习中,一个 样本有多个特征,研究多个特征的概率分布与统计情况

####二维随机变量

avatar

####为什么需要协方差?

avatar

####协方差

avatar

####协方差的性质

avatar

####协方差矩阵

avatar

主成分分析法

#####PCA的意义

avatar

#####PCA的数学模型

avatar

####PCA推导

avatar

avatar

avatar

####PCA实施

avatar

avatar

概率论与信息论

####切比雪夫不等式

avatar

####中心极限定理

avatar

avatar

####关于正态分布计算的补充

avatar

####矩的概念

avatar

avatar

####矩估计

avatar

avatar

####极大似然估计的思想

avatar

####极大似然估计

avatar

avatar

####极大似然估计求法

avatar

avatar

avatar

MLE在机器学习中的应用

参数估计 逻辑回归的参数估计

最大后验估MAP

####先验信息

avatar

####先验分布

avatar

####如何利用先验信息?

在样本少的情况下,如何 加入先验信息? 后验概率

####后验概率

avatar

####最大后验估计

avatar

####贝叶斯法则

avatar

avatar

avatar

####贝叶斯意义

avatar

####贝叶斯公式的密度函数形式

avatar

avatar

####共轭分布

avatar

avatar

avatar

####如何度量信息的多少?

avatar

####自信息量

avatar

####信息熵

avatar

avatar

####交叉熵

avatar

####交叉熵在机器学习中的应用

交叉熵损失函数 衡量两个随机变量之间的相似度

####互信息

avatar

####KL散度

avatar

####KL散度的性质

avatar

avatar

##优化方法

优化方法

所谓最优化问题,指在某些约束条件下,决定某些可选择的变量应该取何值,使所选定的目标函数达到最优的问题。即运用最新科技手段和处理方法,使系统达到总体最优,从而为系统提出 设计、施工、管理、运行的最优方案。

为什么要用优化算法?

1
2
3
求导找函数的最小(大)值不行吗?
考虑:1、多元函数
2、局部最大最小值

####线性规划

avatar

avatar

梯度下降法

avatar

一维函数梯度

avatar

####梯度下降法

avatar

avatar

avatar

####梯度法的迭代过程

avatar

####批量梯度下降BGD

avatar

avatar

####随机梯度下降SGD

avatar

avatar

####小批量梯度下降法MBGD

avatar

avatar

牛顿法

求解无约束极值问题得最古老算法之一,已发展成为一类算法:Newton型方法。
在局部,用一个二次函数近似代替目标函数 f(x),然后用近似函数的极小 点作为f(x) 的近似极小点。

avatar

avatar

avatar

avatar

avatar

avatar

avatar

拟牛顿法

拟牛顿法的本质思想是改善牛顿法每次需要求解复杂的Hessian矩阵的逆矩阵的缺陷,它使用 正定矩阵来近似Hessian矩阵的逆,从而简化了运算的复杂度。 拟牛顿法和最速下降法一样只要求每一步迭代时知道目标函数的梯度。通过测量梯度的变化, 构造一个目标函数的模型使之足以产生超线性收敛性。 这类方法大大优于最速下降法,尤其对于困难的问题。另外,因为拟牛顿法不需要二阶导数的 信息,所以有时比牛顿法更为有效。

用不包含二阶导数的矩阵近似Hesse*矩阵的*

avatar

avatar

####常用的拟牛顿法

avatar

共轭方向法

共轭方向法是介于最速下降法与牛顿法之间的一类方法。

它仅需利用一阶导数信息,但克服了最速下降法收敛慢的缺点,又避免了存储和 计算牛顿法所需要的二阶导数信息。

avatar

avatar

####共轭方向法的几何意义

avatar

avatar

avatar

共轭梯度法

共轭梯度法(conjugate gradient method, CG)是以共轭方向(conjugate direction)作为 搜索方向的一类算法。

⚫ CG法是由Hesteness和Stiefel于1952年为求解线性方程组而提出的。后来用于求解无约束最优 化问题,它是一种重要的数学优化方法。这种方法具有二次终止性

CG的基本思想是把共轭性与最速下降法相结合,利用已知迭代点的梯度方向 构造一组共轭方向,并沿着此组方向进行搜索,求出目标函数的极小点。

什么是二次终止性?

如果某算法用于求解目标函数为二次函数的无约束问题时,只需要经过有限迭代就能 达到最优解,则该算法具有二次终止性。
共轭梯度法就有二次终止性

avatar

avatar

avatar

avatar

####动量梯度下降法法Momentum

avatar

avatar

####均方根优化法RMSp

avatar

avatar

####自适应矩估计法Adam

avatar

avatar

####学习率衰减

avatar

avatar

####早停

avatar

核心思想:

如果训练数轮后准确率(损失函数)没有上升(下降),就停止训练

应用场景:

大批量数据,训练时间长

####局部最优值

avatar

####鞍点问题

avatar

大数据基础

pyspark-RDD基础

spark

1
pyspark是spark的python API,允许python调用spark编程模型

初始化spark

SparkContext

1
2
from pyspark import SparkContext
sc = SparkContext(master='local[2]')

核查SparkContext

1
2
3
4
5
6
7
8
9
sc.version									获取SparkContext的版本
sc.pythonVer 获取python版本
sc.master 要连接的Master URL
str(sc.sparkHome) spark工作节点的安装路径
str(sc.sparkUser()) 获取SparkContext的spark用户名
sc.appName 返回应用名称
sc.applicationId 返回应用程序ID
sc.defaultParallelism 返回默认并行级别
sc.defaultMinPatitions RDD默认最小分区数

配置

1
2
3
from pyspark import SparkConf,SparkContext
conf = (SparkConf().setMaster("local").setAppName("my APP").set("spark.executor.memory","1g"))
sc = SparkContext(conf=conf)

使用shell

pyspark shell已经为SparkContext创建了名为sc的变量

1
2
./bin/spark-shell --master local[2]
./bin/pyspark --master local[4] --py-files code.py

用—master参数设定Context连接到哪个Master服务器,通过传递逗号分隔列表至—py-files添加Python.zip、egg或.py文件到Runtime路径

加载数据

并行集合

1
2
3
4
rdd = sc.parallelize([('a',7),('a',2),('b',2)])
rdd2 = sc.parallelize([('a',2),('d',1),('b',1)])
rdd3 = sc.parallelize(range(100))
rdd4 = sc.parallelize([("a",["x","y","z"]),("b",["p","r"])])

外部数据

使用textFile()函数从HDFS、本地文件或其它支持hadoop的文件系统里读取文件,或使用wholeTextFiles()函数读取目录下所有文本文件

1
2
textFile = sc.textFile('a.txt')
textFile2 = sc.wholeTextFiles(/aa)

提取RDD信息

基础信息

1
2
3
4
5
6
7
8
9
10
11
rdd.getNumPatitions()								列出分区数
rdd.count() 计算RDD的实例数量
rdd.countByKey() 按键计算RDD实例数量
defaultdict(<type 'int'>,('a':2,'b':1))
rdd.countByValue() 按值计算RDD实例数量
defaultdict(<type 'int'>,(('b',2):1,('a',2):1,('a',7):1))
rdd.collectAsMap() 以字典的形式返回键值
('a':2,'b':2)
rdd.sum() 汇总RDD元素
4959
sc.parallelize([]).isEmpty() 检查RDD是否为空

汇总

1
2
3
4
5
6
7
rdd.max()					RDD元素的最大值
rdd.min() RDD元素的最小值
rdd.mean() RDD元素的平均值
rdd.stdev() RDD元素的标准差
rdd.variance() RDD元素的方差
rdd.histogram(3) 分箱(bin)生成直方图
rdd.stats() 综合统计包括:计数、平均值、标准差、最大值和最小值

应用函数

1
2
3
4
rdd.map(lambda x:x+(x[1],x[0])).collect()		对每个RDD元素执行函数
rdd.flatMap(lambda x:x+(x[1],x[0])) 对每个RDD元素执行函数,并拉平结果
rdd.collect()
rdd.flatMapValues(lambda x:x).collect() 不改变键,对rdd的每个键值对执行flatMap函数

选择数据

1
2
3
4
5
6
7
8
9
10
11
获取
rdd.collect() 返回包含所以RDD元素的列表
rdd.take(4) 提取前4个RDD元素
rdd.first() 提取第一个RDD元素
rdd.top(2) 提取前两个RDD元素
抽样
rdd.sample(False,0.15,81) 返回RDD的采样子集
筛选
rdd.filter(lambda x:'a' in x) 筛选RDD
rdd.distinct() 返回RDD里的唯一值
rdd.keys() 返回RDD键值对里的键

迭代

1
2
def g(x):print(x)     
rdd.foreach(g)

改变数据形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
规约
rdd.reduceByKey(lambda x,y:x+y) 合并每个键的值
rdd.reduce(lambda x,y:x+y) 合并RDD的值
分组
rdd.groupBy(lambda x:x%2).mapValues(list) 返回RDD的分组值
rdd.groupByKey().mapValues(list) 按键分组RDD
集合
seqOp = (lambda x,y:(x[0]+y,x[1]+1))
combOP = (lambda x,y:(x[0]+y[0],x[1]+y[1]))
rdd.aggregate((0,0),seqOp,combOP) 汇总每个分区里的RDD元素,并输出结果
rdd.aggregeteByKey((0,0),seqOp,combOP) 汇总每个RDD的键的值
rdd.fold(0,add) 汇总每个分区里的RDD元素,并输出结果
rdd.foldByKey(0,add) 合并每个键的值
rdd,keyBy(lambda x:x+x) 通过执行函数,创建RDD元素的元组

数学运算

1
2
3
rdd.subtract(rdd2)							返回RDD2里没有匹配键的rdd的兼职对
rdd2.subtractByKey(rdd) 返回rdd2里的每个(键、值)对,rdd中,没有匹配的键
rdd.cartesian(rdd2) 返回rdd和rdd2的笛卡尔积

排序

1
2
rdd.sortBy(lambda x:x[1])					按给定函数排序RDD
rdd.sortByKey() 按键排序RDD的键值对

重分区

1
2
rdd.repartition(4)							新建一个含4个分区的RDD
rdd.coalesce(1) 将RDD中的分区数缩减为1个

保存

1
2
rdd.saveAsTextFile("rdd.txt")
rdd.saveAsHadoopFile("hdfs://namenodehost/parent/child",'org.apache.hadoop.mapred.TextOutputFormat')

终止SparkContext

1
sc.stop()

执行程序

1
./bin/spark-submit examples/src/main/python/pi.py

Pyspark_sql

Pyspark与Spark SQL

Spark SQL是Apache Spark处理结构化数据的模块

初始化SparkSession

SparkSession用于创建数据框,将数据框注册为表,执行SQL查询,缓存表及读取Parquet文件

1
2
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("my app").config("spark.some.config.option","some-value").getOrCreate()

创建数据框

从RDD创建
1
2
3
4
5
6
7
8
9
10
11
12
13
from pyspark.sql.types import *
推断Schema
sc = spark.sparkContext
lines = sc.textFile("people.txt")
parts = lines.map(lambda l:l.split(","))
people = parts.map(lambda p:Row(name=p[0],age=int(p[1])))
peopledf = spark.createDataFrame(people)
指定Schema
people = parts.map(lambda p:Row(name=p[0],age=int(p[1].strip())))
schemaString = "name age"
fields = [StructField(field_name,StringType(),True) for field_name in schemaString.split()]
schema = StructType(fields)
spark.createDataFrame(people,schema).show()
从spark数据源创建
1
2
3
4
5
6
7
8
json
df = spark.read.json("customer.json")
df.show()
df2 = spark.read.load("people.json",format = "json")
Parquet文件
df3 = spark.read.load("users.parquet")
文本文件
df4 = spark.read.text("people.txt")
查阅数据信息
1
2
3
4
5
6
7
8
9
10
11
12
df.dtypes						返回df的列名与数据类型
df.show() 显示df内容
df.head() 返回前n行数据
df.first() 返回第一行数据
df.take(2) 返回前两行数据
df.schema 返回df的schema
df.describe().show() 汇总统计数据
df.columns 返回df列名
df.count() 返回df的行数
df.distinct().count() 返回df中不重复的行数
df.printSchema() 返回df的Schema
df.explain() 返回逻辑与实体方案
重复值
1
df = df.dropDuplicates()
查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pyspark.sql import functions as F
Select
df.select("firstName").show() 显示firstName列的所有条目
df.select("firstName","lastName".show())
df.select("firstName","age",\
explode("phoneNumber")\ 显示firstName、age的所有条目和类型
.alias("contactInfo"))\
.select("ContactInfo.type","firstName","age")
df.select(df["firstName"],df["age"]+1).show() 显示firstName和age列的所有记录添加
df.select(df["age"]>24).show() 显示所有小于24的记录
When
df.select("firstName",F.when(df.age>30,1))\ 显示firstName,且大于30岁显示1,小于30显示0
.otherwise(0).show()
df[df.firstName.isin("Jane","Boris")].collect() 显示符合特定条件的firstName列的记录
Like
df.select("firstName",df.lastName,\ 显示lastName列中包含Smith的firstName列的记录
like("Smith")).show()
Startswith-Endwith
df.select("firstName",df.lastName.\ 显示lastName列中以Sm开头的firstName列的记录
startswith("Sm")).show()
df.select(df.lastName.endswith("th")).show() 显示以th结尾的lastName
Substring
df.select(df.firstName.substr(1,3).alias("name"))返回firstName的子字符串
Between
df.select(df.age.between(22,24)).show() 显示介于2224直接的age列的所有记录

添加、修改、删除列

添加列
1
2
3
4
5
6
df = df.withColumn('city',df.address.city) \
.withColumn('postalCode',df.address.postalCode) \
.withColumn('state',df.address.state) \
.withColumn('streetAddress',df.address.streetAddress) \
.withColumn('telePhoneNumber',explode(df.phoneNumber.number)) \
.withColumn('telePhoneType',explode(df.phoneNumber.type)) \
修改列
1
df = df.withColumnRenamed('telePhoneNumber','phoneNumber')
删除列
1
2
df = df.drop("address","phoneNumber")
df = df.drop(df.address).drop(df.phoneNumber)
分组
1
df.groupBy("age").count().show()		按age列分组,统计每组人数
筛选
1
df.filter(df["age"]>24).show()			按age列筛选,保留年龄大于24岁的
排序
1
2
3
peopledf.sort(peopledf.age.desc()).collect()
df.sort("age",ascending=False).collect()
df.orderBy(["age","city"],ascending=[0,1]).collect()
替换缺失值
1
2
3
df.na.fill(50).show()				用一个值替换空值
df.na.drop().show() 去除df中为空值的行
df.na.replace(10,20).show() 用一个值去替换另一个值
重分区
1
2
df.repartition(10).rdd.getNumPartitions()	将df拆分为10个分区
df.coalesce(1).rdd.getNumPartitions() 将df合并为1个分区

运行SQL查询

将数据框注册为视图
1
2
3
peopledf.createGlobalTempView("people")
df.createTempView("customer")
df.createOrReplaceTempView("customer")
查询视图
1
2
df = spark.sql("select * from customer").show()
peopledf = spark.sql("select * from global_temp.people").show()

输出

数据结构
1
2
3
rdd1 = df.rdd		将df转为rdd
df.toJSON().first() 将df转为rdd字符串
df.toPandas() 将df的内容转为Pandas的数据框
保存至文件
1
2
df.select("firstName","city").write.save("nameAndCity.parquet")
df.select("firstName","age").write.save("nameAndAges.json",format="json")
终止SparkSession
1
spark.stop()

数据分析常用工具总结

numpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 优点:向量化数据操作比for循环,速度大大加强,numpy array比list好的地方在于切片
2. array属性
np.random.random((2,2)) # 0-1随机数
np.random.randint(1,10,(3,3)) # 随机整数
array.shape, array.dtype # numpy两个属性
array.astype(np.float64) # 类型转换
3. array切片操作
a[0,1] # 第一个维度为0,第二个维度1,第三个维度全选,类似于a[0,1,:]
a[a>2] # boolean indexing, 利用broadcasting进行判断, 之后可以作为index进行数据的提取
a[a>2]=0 # 也可以对满足条件的元素进行赋值
4. array数学运算
broadcasting, 对不匹配的数据在高维上进行扩展,在取最小公倍数
np.sum(array) # 统计运算
np.dot # 矩阵乘法,点乘
np.multiply # 逐个元素乘法,对应相乘

#pandas

1
2
3
基于Numpy构建,利用它的高级数据结构和操作工具,可使数据分析工作变得更加便捷高效。
import numpy as np
import pandas as pd

基本数据结构

Series

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 基本概念
pd.__version__ # 查看版本
pd.Series # 可以使用不同类型,和list区别在于有index, 可以指定index

2. Series构建
pd.Series([1,2,3], index=['a', 'b', 'c'])
pd.Series({...}, name="xxx") # 通过对dictionary进行构建pandas, 给Series赋予名字

3. 切片
aseries[[1,4,3]]; aseries[1:]; aseries[:-1] # 数字下标切片,即使index不是数字也ok

4. 运算规则
series的相加是根据index对应相加的

5. 取值
数学运算也是broadcasting方式
'xxx' in aseries # 判断xxx是否在aseries的index中
aseries.get('xxx', 0) # 类似于字典
aseries[aseries<20] # boolean index也可以
aseries.median() # 除去缺失值之后进行统计运算
aseries['xxx'] = 1000 # 对aseries['xxx']重新赋值
np.square(aseries) # 对每个运算进行计算平方

DataFrame

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
1. 基本概念
一组Series集合在一起

2. DataFrame的构建
- pd.DataFrame({'a':[1,2,3], 'b':[1,4,3]}, columns = ['b', 'a'], index = ['one', 'two', 'three']) # 构建DF, 指定列名以及index名
- pd.DataFrame([{'a':100,'b':200}, {'a':200, 'b':300}], index=['one', 'two']) # 按照一行一行构建DF
- pd.DataFrame({'a':seriesa, 'b':seriesb} # 记住按照index对齐, 缺失值直接Nan填充

3. 元素的提取以及增加及逻辑操作及转置
- aDF['xxx']/aDF.xxx # 取出来的是一个Series
- aDF[['xxx']] # 取出来的是一个DF
- aDF.loc(['a','b'],['c','d']) # 取对应的数据
- aDF.loc[:, 'newcol'] = 2000 # 如果没有newcol那么就新加一列
- aDF.loc[(aDF['a']>10) & (aDF['b']<100), :] # 也可以给条件进行筛选,& | ~进行逻辑运算
- aDF.T # 进行转置
4. 数据读入以及基本信息以及删除
- pd.read_csv(path, sep='\t', index_col=''/int, usecols=[...], header=0, parse_dates=[0]/['Date']) # 读文件,第一列作为日期型,日期型处理参照: http://hshsh.me/post/2016-04-12-python-pandas-notes-01/
- aDF.to_csv('xxx.csv', sep='\t', index=True, header=True) # 写文件
- aDF.describe(include=[np.float64...]) / aDF.info() # 对数据进行统计,查看缺失值
- aDF.shape
- aDF.isnull() # 判断是是否为空
- aDF[aDF['xxx'].isnull(), :] = 10 # 对空值赋值
- aDF.notnull() # 查看是否有值
- aDF.drop(['one', 'two'], axis=0) # 对index为one和two的两行进行删除, axis=1删除列

5. 数据分组聚合
- aDF.groupby('name', sort=False).sum() # 对DF进行聚合操作,同时对相应聚合的列进行排序,然后计算其他值的和
- groupbyname=aDF.groupby('name'); groupbyname.groups; len(groupbyname) # 得到对应的各个组别包含的index, 并且可以获取对应的group长度
- aDF.groupby('name').agg([np.sum, np.mean, np.std]) # 对不同类别的数据进行各类运算, 每个name对应三列分别是分组之后np.sum, np.mean, np.std计算
- aDF.groupby('name').agg(['sum', 'median', 'mean']) # 和上面的作用相同
- aDF.groupby('name').agg(['a':np.sum, 'b':median, 'c':np.mean]) # 对不同列进行不同操作
- aDF.groupby(['name', 'year']).sum()/mean()/median()/describe() # 多组分类
- aDF.groupby(['name', 'year']).size() # 多组分类, 每一组有多少个记录
- 提取group类别名称以及类别对应的数据行
for name,group in groupbyname:
print(name) # 类别名称
print(group) # 名称对应的数据行
groupbyname.get_group('jason') # 可以得到对应组别的数据行,DF格式
6. transform/apply/filter 数据变换
transfrom可以对分组进行变换, apply对整个DF进行分类,filter对分组进行判断
- aDF['Date'].dt.dayofweek # 可以得到对应的日期中的第几天
- aDF.groupby(aDF.index.year).mean() # 可以对相应的日期型的年进行分组聚合
- aDF.groupby(aDF.index.year).transform(lambda x: (x-x.mean())/x.std()) # 对每一年的数据求均值以及标准差,并对每个数据进行操作,之所以没以每年为单位进行展示主要是跟function有关,因为之前的是mean之类的
- aDF.groupby(aDF.index.year).apply(lambda x: (x-x.mean())/x.std()) # 可以起到相同的效果
- aDF.loc[:,'new'] = aDF['xxx'].apply(afunc) # 可以对xxx这一列进行操作按照afunc进行操作,然后创建新的列
- aSer = pd.Series([1,1,2,2,2,3,3,4,5,5]); sSer.groupby(sSer).filter(lambda x:x.sum()>4) # 对ser进行过滤,留下那些和大于4的类别

7. 表格的拼接与合并(concat/append/merge/join)
- df1.append(df2, sort=False, ignore_index=True) # 追加在行上,同时忽略原先df1和df2的index,合并为新的index
- df1.append([df2, df3]) # 也可以追加两个DF, 参考: https://zhuanlan.zhihu.com/p/38184619
- pd.concat([df1.set_index('a'), df2.set_index('a')], sort=False, axis=1, join='inner') # 和上述利用merge在a字段上进行内连接的效果类似,因为concat是基于index进行连接的,merge可以不基于index,指定字段
- pd.concat([df1, df2, df3], keys=['a', 'b', 'c'], axis=0, join='outer', sort=False) #列对齐的方式对行进行拼接,缺少值则补充为None,可以对拼接的每个df进行key的命名,axis=1的时候行对齐列拼接; join指定连接方式,outer表示外连接,inner表示内连接,sort是否对合并的数据进行排序
- merge # 基于某个字段进行连接,之前的append和concat都是在行上或者列上进行连接的,merge类似于SQL里面的连接,可以指定某个字段或某几个字段,体现在on上,on接list就是多个字段为key
- pd.merge(df1, df4, on='city', how='outer'/'inner'/'left'/'right') # 基于两个表中的city字段进行表格的连接,把其他的列进行combine到一起,不指定on的话就会找字段相同的那个进行拼接,注意concat是基于index进行拼接的
- pd.merge(df1, df2, how='inner', left_index=True, right_on='id') # 对数据进行merge,左表以index作为连接关键字,右表用id作为关键字
8. 链家Case study流程
- pd.to_datetime() # 日期类型转换
- df.drop(droplist, inplace=True, axis=1) # 删除一些列
- aDF.describe(include='all') # 字符串变量也会同时统计
- aDF.sort_values(by = 'xxx').tail() # 找出更新最晚的20套,但是有可能同一天超过20套
- 如果对数据进行处理发现转换未果可能是因为数据有缺失,做异常处理,缺失值作为Nan
- aDF.nsmallest(columns='age', n=20) # 取出年龄最小的20个数据
- groupby().agg() 之后一般会使用reset_index() 对数据进行归置然后再进行操作,ascending=False
- adf.value_counts(normalize=True) # 默认是按照value进行排序的
- aDF.apply(lambda x: 'xxx' in x) # 筛选出xxx在某列的值中与否,返回Ture, False,正则表达式的字符串匹配
- 可以定义正则表达式对文本信息进行提取
def get_info(s, pattern, n):
result = re.search(pattern, s)
if result:
return result.group(n)
else:
return ''
- .astype(int) # 转换pd类型
- help(pd.Series.value_counts) # 打印帮助文档

python绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
1. pandas 绘图
- pd.date_range('2018/12/28', periods=10) # 产生日期格式, 以2018/12/28为起始产生以天为单位的日期时间list
- pandas绘图需要把横坐标作为index,之后再画图
- 折线图绘制需要注意各列幅度,否则数值不明显
- df.plot.bar() # barplot, stacked=True, 堆叠
- df.plot.barh() # 绘制水平的barplot
- df.plot.hist(bins = 20) # 绘制直方图,单维度
- df.plot.box() # 对每列去看一些分布outlier
- df.plot.area # 堆叠区域图
- df.plot.scatter(x='a', y='b') # 散点图
- df.plot.pie(subplots=True) # 绘制带图例的饼图

2. matplotlib 绘图
- plt.rcParams['figure.figsize'] = (12,8) / plt.figure(figsize=(12,8)) # 设置画布大小
- ax = plt.plot(x,y,color='green', linewidth='-', marker='./*/x', label=r'$y=cos{x}$'/r'$y=sin{x}$'/r'$y=\sqrt{x}$') # 绘图
- ax.spines['right'].set_color('none') # 去掉右边的边框
- ax.xaxis.set_ticks_position('bottem') # ??????????????
- plt.xticks([2,4,6], [r'a',r'b',r'c']) # 设置坐标轴刻度
- ax.spines['bottem'].set_position('data', 0) # 设置坐标轴从0开始
- plt.xlim(1,3) # 设置坐标位置
- plt.title() # 标题
- plt.xlabel(r'xxx', fontsize=18, labelpad=12.5) # 绘制label, r值的是不转义的,$$值的是markdown格式
- plt.text(0.8, 0.9, r'$$', color='k', fontsize=15) # 进行注解
- plt.scatter([8], [8], 50, color='m') # 在某个位置,点有多大,颜色是什么
- plt.annotate(r'$xxx$', xy=(8,8), xytext=(8.2, 8.2), fontsize=16, color='m', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.1', color='m')) # 对某个点进行注解, 进行加箭头等等
- plt.grid(True) # 网格线
- plt.plot(x, y) # xy应为np array,如果是pandas那么可以通过values进行取值转换
3. matplotlib 绘图case
- 文件解压
x = zipfile.ZipFile(xxx, 'r') # 解压文件夹
x.extractall('xxxdir') # 解压到某个文件夹下
x.close() # 记得关闭
- matplotlib.rc('figure', figsize=(14,7)) # 设置一下图片尺寸
- matplotlib.rc('font', size=14) # 设置字体
- matplotlib.rc('axes.spines', top=False, right=False) # 设置边线
- matplotlib.rc('axes', grid=False) # 设置网格
- matplotlib.rc('axes', facecolor='white') # 设置颜色
- fig,ax含义
fig,ax = plt.subplots() # 创建绘图对象之后对ax进行操作,相当于先fig=plt.figure()再ax=fig.add_subplot(1,1,1)
https://blog.csdn.net/htuhxf/article/details/82986440
- ax.fill_between(x, low, upper, alpha=) # 对回归进行置信度绘制
- ax2 = ax1.twinx() # 共享同一个x轴
- ax2.spines['right'].set_visible(True) # 对右侧坐标轴进行设置,得到相应的图
- 图的使用
关联分析:散点图,曲线图,置信区间曲线图,双坐标曲线图
分布分析:堆叠直方图, 密度图
组间分析:柱状图(带errorbar),boxplot,这个需要多看看,

4. seaborn 绘图
- 引入seaborn的同时也要引入matplotlib因为,是底层
- 颜色设置
sns.set(color_codes=True) # 一些集成的颜色
https://seaborn.pydata.org/tutorial/color_palettes.html
- sns.displot(x, kde=True, bins=20, rug=True, fit=stats.gamma) # histgram加密度线,样本分布情况, 拟合某些分布fit
- sns.kdeplot # 类似于上面的,kde是每个样本用正态分布画,如果样本多,高度就高,之后再做归一化
- sns.jointplot(x,y,data) # 绘制带有histgram以及散点图的图,两个变量
- sns.pairplot(df) # 直接绘制各个列之间的散点图以及对应的histgram,多个变量
- scatter plot的密度版
with sns.axes_style('ticks'):
sns.jointplot(x,y,data, kind='hex'/'kde',color='m') #相当于对点很多的时候,六角箱图就能体现出点的多少,kde是等高线,密度联合分布
- 多图绘制1
g = sns.PairGrik(df) # 各个列混合,产出n*n个格子
g.map_diag(sns.kdeplot) # 对角线绘制
g.map_offdiag(sns.kdeplot, cmap='Blues_d', n_levels=20) # 绘制对角线是kde密度图其他为等高线的图
- 多图绘制2
g = FaceGrid(row=[..],aspect=1.5, data=)
g.map(sns.boxplot, x, y, hue, hue_order=[], ...)
- 多图绘制3
g = sns.PairGrid(data, x_vars=[], y_vars=[], aspect=0.5, size=3.5)
g.map(sns.violinplot, palette='bright') # x_vars数量*y_vars数量个子图,然后每个子图都绘制violinplot
- 关联分析 sns.lmplot
· sns.lmplot(x, y, data) # 散点图+线性回归,95%置信区间,适用于连续值
· sns.lmplot(x, y, data, x_jitter=0.08) # 左右抖动, 点如果离得近,会把点左右抖动开,适用于离散值
· sns.lmplot(x, y, data, x_estimator=np.mean, ci=95, scatter_kws={'s':80}, order=2, robust=True) # 对于离散值还可以这样操作,先求均值和95置信区间,之后再进行拟合, scatter_kws对点进行操作,order是说对数据点进行二次方的分布,而不是线性分布,robust打开的作用是踢除异常点,然后再进行绘制图
· sns.lmplot(x, y, data, x_estimator=np.mean, ci=95, scatter_kws={'s':80}, order=1, robust=True, logistic=True) # 相当于是说对二值化的数据进行logistic回归拟合,sigmoid拟合
· sns.lmplot(x, y, data, hue, col, row, col_wrap, aspect=0.5) # 散点图.线性回归,95%置信区间,适用于连续值,hue进行分组类似于pandas里面的groupby, hue变量一定是个离散变量, col也可以加一个变量,可以把图分成多列,row可以多行,如果row,col以及hue都指定,那么相当于在pandas里面groupby三个内容,col_wrap用于之指定每个col中的绘图数量
- sns.residplot() # 残差图
- sns.barplot(x,y,hue,ci=None) # 是否打开置信区间
- sns.stripplot(x, y, data, jitter =True) # 基于x为离散数据的,类似于散点图的boxplot
- sns.swarmplot(x, y, data) # 蜂群图,类似于小提琴图的点版
- sns.boxplot()
- sns.violinplot(bw) # 属于kde以及boxplot的组合,既看了单变量分布,也看了各变量之间的差异
- sns.violinplot(split=True, hue, inner='stick') # split将hue为两个类型的进行拼接绘制小提琴图,stick,每个样本绘制竖线
- sns.countplot(x, data) # 绘制离散变量数量分布,类似于value_counts(),类似于barplot但是使用的统计量是数量
- sns.pointplot(x, y, hue) # 查看离散变量x以及hue在离散变量y上的差别,使用均值,画点
- sns.factorplot(x, y, hue, col, data, kind='swarm') # 是一种泛化的绘图函数
- a.savefig('xx') # 进行图片存储 plt函数

python基础知识整理

#Canda环境安装以及包管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
清华镜像下载https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/
命令行启动jupyter notebook或点击快捷图标方式启动
conda list # 查看所在环境的安装的包
conda upgrade --all # 对包进行更新
spyder # 启动anaconda中的IDE
conda install numpy pandas # 在某个环境下能够安装某些Python包
conda install numpy=1.10 # 安装特定版本的包
conda remove < package_name > # 删除包
conda env list # 列出当前机器上创建的虚拟环境
conda create -n env1 python=2.7 # 创建一个名为env1的环境然后在其中安装python2.7
conda create -n env1 numpy # 创建一个名为env1的环境然后在其中安装numpy
source activate env1 # 进入env1虚拟环境,在window上不用加source
source deactivate # 离开环境
conda install -n py27 ipykernel # 在虚拟环境py27下安装ipykernel
python -m ipykernel install --user --name py27 --display-name "python2" # 在py27环境内安装ipykernel并在菜单里命名为python2
conda env remove -n py27 # 移除py27的虚拟环境
conda install jupyter notebook # 在conda环境中安装jupyter notebook
%matplotlib # jupyter notebook中已交互式方式实现matplotlib的绘图
%matplotlib inline # 不跳出,直接内嵌在web中

jupyter notebook常用配置

notebook中的magic开关

1
2
为实现一些快捷操作,提升效率。notebook中提供magic开关,能极大的优化使用notebook的体验。
magic开关分为两大类:%line magic & %%cell magic

magic开关总览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
%quickref# 所有magic命令 
%lsmagic# 打印所有magic命令

%config ZMQInteractiveShell.ast_node_interactivity='all'/'last_expr'
%pprint # 打印所有结果,保证每次执行都输出,默认只输出最后一个内容
%config ZMQInteractiveShell可以查看可选择的输出类型
或者执行这个命令保证多输出
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'/'last_expr'

%%整个cell magic
%%writefile test.py # 将cell中的命令写入文件test.py
%%timeit代码计时
%%bash # 在cell内可以执行bash命令
%%writefile xx.py # 把整个cell中的内容输入到xx.py中,如果新加内容可以%%writefile -a xx.py

%line magic命令
%matplotline inline # 在jupyter内打印图片
%run utils.ipynb # 执行本地的utils.ipynb文件,进行配置

line magic和cell magic区别就在于line magic只在一行有效,cell magic在多行都有效
具体参考:https://gispark.readthedocs.io/zh_CN/latest/pystart/jupyter_magics.html

Jupyter notebook扩展

1
2
3
4
5
6
7
8
jupyter_contrib_nbextensions
直接安装官网conda命令安装即可
conda install jupyter notebook
conda install -c conda-forge jupyter_contrib_nbextensions
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple autopep8
pip安装加速镜像:https://www.cnblogs.com/microman/p/6107879.html
jupyter 使用参考资料:https://zhuanlan.zhihu.com/p/33105153
jupyter extension 参考资料:https://zhuanlan.zhihu.com/p/52890101

jupyter 使用linux命令

1
!head -n 5 xx.txt # 直接通过jupyter行使linux命令

python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
python语言是一种面向对象、动态数据类型的解释型语言
1.运行方式
解释运行:直接py脚本运行
交互运行:jupyter输入一个输出一个

2.命名规则:
常量大写,下划线隔开单词
类用驼峰命名
del xx 删除变量xx

3.操作优先级:
函数调用,寻址,下标
幂运算
翻转运算符
正负号
* / %
- +
4.赋值
多重赋值:a=b=10相当于a=10,b=10
多元赋值 a,b,c = 1,2,3
交换赋值 a,b = b,a # 指针

5.解包(需要拓展 参考:https://zhuanlan.zhihu.com/p/33896402?utm_source=wechat_session&utm_medium=social&s_r=0)
l1 = [1,2,3,4,5,'6']; a,b,*c,d = l1
l1=[1,2,3,4];b='sdaad';[*l1,*b]
b,=[[3,4,5]] # 逗号解包

6.python进制及基本类型
bin() 二进制
oct() 八进制
hex() 十六进制

float('inf') 正无穷

基本操作

1
2
3
4
5
6
7
8
9
#行内注释
""" """多行注释
?内省,显示对象的通用信息
??内省,显示出大部分函数的源代码
help()显示一个对象的帮助文档
%timeit 魔法命令,计算语句的平均执行时间
type(x)查看变量x的数据类型
in(x) 将变量x的数据类型转换为整型
isinstance(x,float)检测变量x是否为浮点型,返回一个布尔型数值

##字符串

1
2
3
4
5
6
7
8
9
10
11
s=u" "定义Unicode字符串
s=r" "定义原始字符串,避免字符串中的字符串转义,在正则表达式中经常使用到
len(s)返回s字符串的字数
s.lower()字母全部转为小写
s.upper()字母全部转为大写
s.capitalize()将字符串s中的首个字符转换为大写,其余部分转换为小写
s.replace('k','l')使用字符"l"替换掉s中所有的字符"k",返回结果是cooldata
s.strip()去掉s最前面和最后面的空格
s.split("\t")使用制表符"\t"分割字符串
'%s is No.%d'%(s,1)格式化
'{} is No.{}'.format(s,1)格式化

列表

1
2
3
4
5
6
7
8
9
10
11
12
list()空列表
l[-1]返回列表的最后一个元素
l[1:3]返回列表的第二个和第三个元素
len(l)返回列表长度
l[::-1]将列表进行逆序排列
l.reverse()将列表进行逆序排列
l.insert(1,"b")在指定的索引位置插入'b'
l.append()在列表末尾添加元素
l.extend()等价于"1+L",将列表L中的元素依次添加到1的末尾
l.remove()删除列表中的某个元素
l.pop()等价于del l[],删除列表中对应索引位置的元素
" ".join([''c,'o','o','k'])将列表中的各个字符串元素用空格连接起来并转换为字符串,返回结果为cook

条件判断和循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if condition1:
statement1
elif condition2:
statement2
else:
statement3

for item in sequence:
statement

while condition:
statement

range(5)产生一个从0到5且间隔为1的整数列表[0,1,2,3,4]
break从最内层for循环或while循环中跳出
continue继续执行下一次循环
pass占位符

enumerate()and zip()

1
2
3
4
5
6
7
8
for i,item in emumerate(l)在每一次循环时取出索引号和相应的值分别赋给和item
s={item**2 for item in l}集合推导式,对l中的每一个元素取平方得到的新集合
D={key:value for key,value in zip(l,k)}字典推导式,通过zip()函数将两个列表l和k中的元素组成键对并形成字典
enumerate(list/set, start=0) # 遍历元素,start指定从哪个数字作为开始下标
c = list(zip(a,b))
c = set(zip(a,b))
c = dict(zip(a,b))
list(zip(*c)) # 解压

推导式

1
2
3
4
5
6
L=[item**2 for item in l]列表推导式,对l中每一个元素取平方得到新的列表
S={item**2 for item in l}集合推导式,对l中的每一个元素取平方得到新的集合
D={key:value for key,value in zip(l,k)}字典推导式,通过zip()函数将两个列表l和k中的元素组成键值对并形成字典
[i for i in range(30) if 1%2==0] # 取0-29之间偶数
[function(i) for i in range(30) if 1%2==0] # function可以自己定义
[ x**2 if x%2 ==0 else x**3 for x in range(10)] # 两个条件

文件读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
读取文件
f=open(filename,mode)返回一个文件对象f,读文件"mode=r",写文件"mode=w"
f.read(size)返回包含前size个字符的字符串
f.readline()每次读取一行,返回该行字符串
f.readlines()返回包含每个文件内容的列表,列表的元素为文件的每一行内容所构成的字符串
f.close()关闭文件并释放它所占用的系统资源
with open("aa.txt","r") as f:
content = f.readlines()
在with主体块语句执行完后,自动关闭文件并释放占用的系统资源
import csv
f=open("aa.csv","r")
csvreader = csv.reader(f)
content_list=list(csvreader)
读取csv文件,并把数据存储为一个嵌套列表(列表的元素扔是一个对象)content_list

写入文件
f.write(s)
print(s,file=f)
两种等价的方式,将字符串s写入文件对象f中

函数

1
2
3
4
def sum(a,b=1)
return a+b
def sum(*args,**kwargs)不定长参数,*args接收包含多个位置参数的元组,**kwargs接收包含多个关键字参数的字典。
obj.methodname一个方法是一个"属于"对象并被命名为obj.methodname的函数

map()和lambda()

1
2
3
4
5
6
7
8
9
map(func,sequence)将函数依次作用在序列的每个元素上,把结果作为一个新的序列返回。
lambda a,b:a+b匿名函数,正常函数定义的语法糖
lambda [参数列表]:表达式
例如: sum = lambda x,y:x+y # 可以有多个参数,返回只能有一个式子
可以作为一个函数的参数赋给另外一个参数
当然普通函数也可以作为参数传入
a = [{'name':'ss','age':10},{'name':'yy','age':7},{'name':'zz','age':15}] # 将匿名函数作为参数传入第三方函数的参数
a.sort(key=lambda x:x['age']) # sort方法需要传入一个key,这个key可以作为排序依据,lambda可以提取每个元素,并对元素排列
a.sort(key=lambda x:x['age'], reverse=True) # 降序

包模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
包是一个文件夹
模块是不同的python文件
import package.module.func()
import package1.module1, package2.module1 # 多个模块调用
import package1.module1 as p1m1 # 对模块进行重命名使用
from package.module import func1 # 调用某个包某个模块的某个函数
import sys;sys.path # 搜索模块路径,包含当前文件夹
package.module.__file__ # 可以确定当前的模块所在的路径
__init__.py #在包被加载的时候,会被执行。在一个包下面可以有也可以没有__init__.py
from package import * # 引用包下面所有的模块都加载,自动搜索是不会发生的,
需要我们在__init__.py下进行定义才可以实现,定义的内容是__all__=["module1","module2"],
将package下的module1和module2都加载进来
如果想直接加载某个函数,在__init__.py里面加入from .module1 import func1, __all__=["func1"]
这样修改完之后,可以直接from package import *,然后直接调用func1即可,不用带package.module
restart kernel
如果想要直接引用包,如:import package,这样的话,需要一定要有__init__.py,否则会在打印package.__file__的时候报错。
注意import package和from package import *效果相同

Counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
字典的继承类
set dict list tuple 作为key
from collection import Counter # 导入
cnt = Counter()
for i in [1,1,2,2,2,3]:
cnt[i] += 1
print cnt
如果用key的话会报错先做第一次初始化才行
cnt2 = Counter(alist) #可以统计每个元素出现的次数(字符串,set,list,)
Counter(cat=4,dogs=8,abc=-1) # 初始化counter次数,或者用dictionary构建
Counter({'cat':4,'dogs':8,'abc':-1})
Counter返回一个字典,如果缺失的话会返回0
del cnt2['xx']
.values()
list(cnt),set(cnt),dict(cnt) # 前两个只返回key
cnt.most_common()[0] # 对出现次数排序
cnt.clear()
cnt1+cnt2 # 对于key相同的value做加法,如果为0则不保留
cnt1-cnt2 # 对于key相同的value做减法
& # 求key相同value的最小值
| # 求key相同value的最大值

random

1
2
3
4
5
6
7
8
9
import random #引入
random.random() # 0-1
random.uniform(1,10) # 包含1,10的浮点数
random.randint(1,10) # 包含1,10的整数
random.randrange(0,20,3) # 0-20能被3整除的数
random.choice([1,2,3]) # 随机取元素
random.choice("qwdwq") # 随机取元素
random.shuffle([12,3,1,4,2,3]) # 混洗
random.sample([1,2,3,4,5], 3) # 从前面的list中选3个

编码和解码

1
2
3
import chardet
chardet.detect(s)
检测字符串的编码方式

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
try:
statement
except:
pass

try:
statement
except Exception as e:
print(e)

try:
statement
except (Exception1,Exception2) as e:
statement1
else:
statement2
finally:
statement3#无论对错都运行
抛出异常
raise Exception('Oops!')
assert statement,e#若继续运行代码、否则抛出e的错误提示信息

正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import re
raw_s=r'\d{17}[\d|x]|\d{15}'
pattern=re.compile(raw_s)
re.search(pattern,s)
用于匹配身份证号
首先使用原始字符串定义正则表达式;然后编译原始字符为正则表达式Pattern对象;最后对整个字符串s进行模式搜索,如果模式匹配,则返回MatchObject的实例,如果该字符串没有模式匹配,则返回弄none
re.search(r'\d{17}[\d|x]|\d{15}',s)将Pattern编译过程与搜索过程合二为一
re.match(pattern,s)从字符串s的起始位置匹配一个模式,如果匹配不成功返回None
re.findall(pattern,s)返回一个包含所有满足条件的字串列表
re.sub(pattern,repl,s)使用替换字符串repl替换匹配到的字符串
re.split(pattern,s)利用满足匹配模式的字符串将字符串s分隔开,并返回一个列表

1.正则表达式的match与search区别
https://segmentfault.com/a/1190000006736033

2.贪婪匹配与非贪婪匹配的区别
https://segmentfault.com/a/1190000002640851
https://blog.csdn.net/lxcnn/article/details/4756030

3. 练习网站
https://alf.nu/RegexGolf 一个正则表达式练习网站
https://regexr.com/ 验证网站

4. 单字符匹配
. # 匹配出点换行符之外的任意字符
\. # 匹配单个.字符
[abd] # 匹配a/b/d单个字符
\d # 匹配数字, 相当于[1,2,3,4,5,6,7,8,9]
\D # 所有非字符
\s # 空白符,空格 tab等等
\S # 所有非空格
\w # a-z,A-Z,0-9,_
\W # 除了 a-z,A-Z,0-9

5. 数量词用来多匹配
m{2} # 表示匹配两个m
m{2,4} # 表示匹配2/3/4个m,贪婪匹配
m* # 0个或者更多个,贪婪匹配
m+ # 1个或者更多个,贪婪匹配
m? # 0个或者1个
^xx # 文本开头是xx进行匹配
xxx$ # 对结尾进行匹配
(re)su(lt) # group

6. python中的正则表达式步骤
写一个文本pattern
进行匹配
对匹配的文本进行后续操作
例子:
import re
pattern = re.compile(r'hello.*\!') # hello后面有若干个字符串直到有!
match = pattern.match('hello, xxx! how are you?') # 对文本进行匹配
if match: # 是否匹配
print match.group() # 如果匹配上了返回相应的匹配到的部分

7. 使用实例
import re
re.compile(r"""
\d+ # 数字部分
\. # 小数点
\d # 小数部分
""", re.X)
这种模式下可以写注解
re.compile(r"\d+\.\d") # 与这个模式结果一样

8.一些命令
match # 一次匹配结果,从头匹配,开头没有就匹配不上了
search # 所有匹配到的
findall # search返回第一个匹配的结果,findall会返回所有的结果
m=re.match()
m.string # 匹配的字符串
m.group(1,2) # 匹配1和2处字符串

9. 替换和分割
split也可以使用正则表达式进行分割
p = re.compile(r'\d+')
p.split('adwdwad1dawwd23dwadw') # 字符串复杂分割
sub # 用来替换
p = re.compile(r'(\w+) (\w+)')
p.sub(r'\2 \1', s) # 匹配字符串并且在匹配到的字符串处进行前后颠倒
subn # 和sub类似,只不过除了返回替换的远足之外,还返回相应的替换次数,可以p.subn(afunc, s), afunc可以自己定义

日期处理

1
2
3
4
5
6
from datetime import datetimes
format="%Y-%m-%d %H:%M:%S"指定日期格式
date_s=datetime.striptime(s,format)
date_s.year
date_s.month
date_s.now()

python正则表达式

Python正则表达式

by 寒小阳(hanxiaoyang.ml@gmail.com)

正则表达式是处理字符串的强大工具,拥有独特的语法和独立的处理引擎。

我们在大文本中匹配字符串时,有些情况用str自带的函数(比如find, in)可能可以完成,有些情况会稍稍复杂一些(比如说找出所有“像邮箱”的字符串,所有和julyedu相关的句子),这个时候我们需要一个某种模式的工具,这个时候正则表达式就派上用场了。

说起来正则表达式效率上可能不如str自带的方法,但匹配功能实在强大太多。对啦,正则表达式不是Python独有的,如果已经在其他语言里使用过正则表达式,这里的说明只需要简单看一看就可以上手啦。

1.语法

废话少说,直接上技能

下面是一张有些同学比较熟的图,我们俗称python正则表达式小抄,把写正则表达式当做一个开卷考试,显然容易得多。

当你要匹配 一个/多个/任意个 数字/字母/非数字/非字母/某几个字符/任意字符,想要 贪婪/非贪婪 匹配,想要捕获匹配出来的 第一个/所有 内容的时候,记得这里有个小手册供你参考。

img

2.验证工具

我们最喜爱的正则表达式在线验证工具之一是http://regexr.com/

谁用谁知道,用过一次以后欲罢不能。

img

3.挑战与提升

长期做自然语言处理的同学正则表达式都非常熟,曾经有半年写了大量的正则表达式,以至于同事间开玩笑说,只要是符合某种规律或者模式的串,肯定分分钟能匹配出来。

对于想练习正则表达式,或者短期内快速get复杂技能,or想挑战更复杂的正则表达式的同学们。 请戳正则表达式进阶练习

so, 各位宝宝enjoy yourself

img

4.Python案例

re模块

Python通过re模块提供对正则表达式的支持。

使用re的一般步骤是

  • 1.将正则表达式的字符串形式编译为Pattern实例
  • 2.使用Pattern实例处理文本并获得匹配结果(一个Match实例)
  • 3.使用Match实例获得信息,进行其他的操作。

In [13]:

1
2
3
4
5
6
7
8
9
10
11
12
# encoding: UTF-8
import re

# 将正则表达式编译成Pattern对象
pattern = re.compile(r'hello.*\!')

# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match = pattern.match('hello, hanxiaoyang! How are you?')

if match:
# 使用Match获得分组信息
print match.group()
1
hello, hanxiaoyang!

re.compile(strPattern[, flag]):

这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。

第二个参数flag是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如re.I | re.M。

当然,你也可以在regex字符串中指定模式,比如re.compile(‘pattern’, re.I | re.M)等价于re.compile(‘(?im)pattern’)

flag可选值有:

  • re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
  • re.M(MULTILINE): 多行模式,改变’^’和’$’的行为(参见上图)
  • re.S(DOTALL): 点任意匹配模式,改变’.’的行为
  • re.L(LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
  • re.U(UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
  • re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:

In [ ]:

1
2
3
4
regex_1 = re.compile(r"""\d +  # 数字部分
\. # 小数点部分
\d * # 小数的数字部分""", re.X)
regex_2 = re.compile(r"\d+\.\d*")

Match

Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。

match属性:

  • string: 匹配时使用的文本。
  • re: 匹配时使用的Pattern对象。
  • pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
  • endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
  • lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
  • lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。

方法:

  • group([group1, …]):
    获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。
  • groups([default]):
    以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。
  • groupdict([default]):
    返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。
  • start([group]):
    返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。
  • end([group]):
    返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。
  • span([group]):
    返回(start(group), end(group))。
  • expand(template):
    将匹配到的分组代入template中然后返回。template中可以使用\id或\g、\g引用分组,但不能使用编号0。\id与\g是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符’0’,只能使用\g<1>0。

In [14]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello hanxiaoyang!')

print "m.string:", m.string
print "m.re:", m.re
print "m.pos:", m.pos
print "m.endpos:", m.endpos
print "m.lastindex:", m.lastindex
print "m.lastgroup:", m.lastgroup

print "m.group(1,2):", m.group(1, 2)
print "m.groups():", m.groups()
print "m.groupdict():", m.groupdict()
print "m.start(2):", m.start(2)
print "m.end(2):", m.end(2)
print "m.span(2):", m.span(2)
print r"m.expand(r'\2 \1\3'):", m.expand(r'\2 \1\3')
1
2
3
4
5
6
7
8
9
10
11
12
13
m.string: hello hanxiaoyang!
m.re: <_sre.SRE_Pattern object at 0x10b111be0>
m.pos: 0
m.endpos: 18
m.lastindex: 3
m.lastgroup: sign
m.group(1,2): ('hello', 'hanxiaoyang')
m.groups(): ('hello', 'hanxiaoyang', '!')
m.groupdict(): {'sign': '!'}
m.start(2): 6
m.end(2): 17
m.span(2): (6, 17)
m.expand(r'\2 \1\3'): hanxiaoyang hello!

Pattern

Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。

Pattern不能直接实例化,必须使用re.compile()进行构造。

Pattern提供了几个可读属性用于获取表达式的相关信息:

  • pattern: 编译时用的表达式字符串。
  • flags: 编译时用的匹配模式。数字形式。
  • groups: 表达式中分组的数量。
  • groupindex: 以表达式中有别名的组的别名为键、以该组对应的编号为值的字典,没有别名的组不包含在内。

In [15]:

1
2
3
4
5
6
7
import re
p = re.compile(r'(\w+) (\w+)(?P<sign>.*)', re.DOTALL)

print "p.pattern:", p.pattern
print "p.flags:", p.flags
print "p.groups:", p.groups
print "p.groupindex:", p.groupindex
1
2
3
4
p.pattern: (\w+) (\w+)(?P<sign>.*)
p.flags: 16
p.groups: 3
p.groupindex: {'sign': 3}

使用pattern

  • match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])

    :

这个方法将从string的pos下标处起尝试匹配pattern

:

  • 如果pattern结束时仍可匹配,则返回一个Match对象
  • 如果匹配过程中pattern无法匹配,或者匹配未结束就已到达endpos,则返回None。
  • pos和endpos的默认值分别为0和len(string)。
    注意:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符’$’。
  • search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])

    :

这个方法从string的pos下标处起尝试匹配pattern

  • 如果pattern结束时仍可匹配,则返回一个Match对象
  • 若无法匹配,则将pos加1后重新尝试匹配,直到pos=endpos时仍无法匹配则返回None。
  • pos和endpos的默认值分别为0和len(string))

In [18]:

1
2
3
4
5
6
7
8
9
10
11
12
13
# encoding: UTF-8 
import re

# 将正则表达式编译成Pattern对象
pattern = re.compile(r'H.*g')

# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match = pattern.search('hello Hanxiaoyang!')

if match:
# 使用Match获得分组信息
print match.group()
1
Hanxiaoyang
  • split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):
    • 按照能够匹配的子串将string分割后返回列表。
    • maxsplit用于指定最大分割次数,不指定将全部分割。

In [19]:

1
2
3
4
import re

p = re.compile(r'\d+')
print p.split('one1two2three3four4')
1
['one', 'two', 'three', 'four', '']
  • findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])

    :

  • 搜索string,以列表形式返回全部能匹配的子串。

In [21]:

1
2
3
4
import re

p = re.compile(r'\d+')
print p.findall('one1two2three3four4')
1
['1', '2', '3', '4']
  • finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):
    • 搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。

In [23]:

1
2
3
4
5
import re

p = re.compile(r'\d+')
for m in p.finditer('one1two2three3four4'):
print m.group()
1
2
3
4
1
2
3
4
  • sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]):

    • 使用repl替换string中每一个匹配的子串后返回替换后的字符串。
- 当repl是一个字符串时,可以使用\id或\g、\g引用分组,但不能使用编号0。 
- 当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。 count用于指定最多替换次数,不指定时全部替换。

In [26]:

1
2
3
4
5
6
7
8
9
10
11
import re

p = re.compile(r'(\w+) (\w+)')
s = 'i say, hello hanxiaoyang!'

print p.sub(r'\2 \1', s)

def func(m):
return m.group(1).title() + ' ' + m.group(2).title()

print p.sub(func, s)
1
2
say i, hanxiaoyang hello!
I Say, Hello Hanxiaoyang!
  • subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]):
    • 返回 (sub(repl, string[, count]), 替换次数)。

In [28]:

1
2
3
4
5
6
7
8
9
10
11
import re

p = re.compile(r'(\w+) (\w+)')
s = 'i say, hello hanxiaoyang!'

print p.subn(r'\2 \1', s)

def func(m):
return m.group(1).title() + ' ' + m.group(2).title()

print p.subn(func, s)
1
2
('say i, hanxiaoyang hello!', 2)
('I Say, Hello Hanxiaoyang!', 2)

jieba中文处理

jieba中文处理

和拉丁语系不同,亚洲语言是不用空格分开每个有意义的词的。而当我们进行自然语言处理的时候,大部分情况下,词汇是我们对句子和文章理解的基础,因此需要一个工具去把完整的文本中分解成粒度更细的词。

jieba就是这样一个非常好用的中文工具,是以分词起家的,但是功能比分词要强大很多。

1.基本分词函数与用法

jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode)

jieba.cut 方法接受三个输入参数:

  • 需要分词的字符串
  • cut_all 参数用来控制是否采用全模式
  • HMM 参数用来控制是否使用 HMM 模型

jieba.cut_for_search 方法接受两个参数

  • 需要分词的字符串
  • 是否使用 HMM 模型。

该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细

In [1]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# encoding=utf-8
import jieba

seg_list = jieba.cut("我在学习自然语言处理", cut_all=True)
print seg_list
print("Full Mode: " + "/ ".join(seg_list)) # 全模式

seg_list = jieba.cut("我在学习自然语言处理", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式

seg_list = jieba.cut("他毕业于上海交通大学,在百度深度学习研究院进行研究") # 默认是精确模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在哈佛大学深造") # 搜索引擎模式
print(", ".join(seg_list))
1
2
3
Building prefix dict from the default dictionary ...
Loading model from cache /var/folders/pn/xp31896922n9rqxgftrqk3l00000gn/T/jieba.cache
Loading model cost 0.496 seconds.
1
<generator object cut at 0x10bbd91e0>
1
Prefix dict has been built succesfully.
1
2
3
4
Full Mode: 我/ 在/ 学习/ 自然/ 自然语言/ 语言/ 处理
Default Mode: 我/ 在/ 学习/ 自然语言/ 处理
他, 毕业, 于, 上海交通大学, ,, 在, 百度, 深度, 学习, 研究院, 进行, 研究
小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, ,, 后, 在, 哈佛, 大学, 哈佛大学, 深造

jieba.lcut以及jieba.lcut_for_search直接返回 list

In [2]:

1
2
3
4
result_lcut = jieba.lcut("小明硕士毕业于中国科学院计算所,后在哈佛大学深造")
print result_lcut
print " ".join(result_lcut)
print " ".join(jieba.lcut_for_search("小明硕士毕业于中国科学院计算所,后在哈佛大学深造"))
1
2
3
[u'\u5c0f\u660e', u'\u7855\u58eb', u'\u6bd5\u4e1a', u'\u4e8e', u'\u4e2d\u56fd\u79d1\u5b66\u9662', u'\u8ba1\u7b97\u6240', u'\uff0c', u'\u540e', u'\u5728', u'\u54c8\u4f5b\u5927\u5b66', u'\u6df1\u9020']
小明 硕士 毕业 于 中国科学院 计算所 , 后 在 哈佛大学 深造
小明 硕士 毕业 于 中国 科学 学院 科学院 中国科学院 计算 计算所 , 后 在 哈佛 大学 哈佛大学 深造

添加用户自定义词典

很多时候我们需要针对自己的场景进行分词,会有一些领域内的专有词汇。

  • 1.可以用jieba.load_userdict(file_name)加载用户字典
  • 2.少量的词汇可以自己用下面方法手动添加:
    • 用 add_word(word, freq=None, tag=None) 和 del_word(word) 在程序中动态修改词典
    • 用 suggest_freq(segment, tune=True) 可调节单个词语的词频,使其能(或不能)被分出来。

In [3]:

1
print('/'.join(jieba.cut('如果放到旧字典中将出错。', HMM=False)))
1
如果/放到/旧/字典/中将/出错/。

In [4]:

1
jieba.suggest_freq(('中', '将'), True)

Out[4]:

1
494

In [5]:

1
print('/'.join(jieba.cut('如果放到旧字典中将出错。', HMM=False)))
1
如果/放到/旧/字典/中/将/出错/。

关键词提取

基于 TF-IDF 算法的关键词抽取

import jieba.analyse

  • jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())
    • sentence 为待提取的文本
    • topK 为返回几个 TF/IDF 权重最大的关键词,默认值为 20
    • withWeight 为是否一并返回关键词权重值,默认值为 False
    • allowPOS 仅包括指定词性的词,默认值为空,即不筛选

In [6]:

1
2
3
import jieba.analyse as analyse
lines = open('NBA.txt').read()
print " ".join(analyse.extract_tags(lines, topK=20, withWeight=False, allowPOS=()))
1
韦少  杜兰特  全明星  全明星赛  MVP  威少  正赛  科尔  投篮  勇士  球员  斯布鲁克  更衣柜  张卫平  三连庄  NBA  西部  指导  雷霆  明星队

In [7]:

1
2
lines = open(u'西游记.txt').read()
print " ".join(analyse.extract_tags(lines, topK=20, withWeight=False, allowPOS=()))
1
行者  八戒  师父  三藏  唐僧  大圣  沙僧  妖精  菩萨  和尚  那怪  那里  长老  呆子  徒弟  怎么  不知  老孙  国王  一个

关于TF-IDF 算法的关键词抽取补充

  • 关键词提取所使用逆向文件频率(IDF)文本语料库可以切换成自定义语料库的路径
    • 用法: jieba.analyse.set_idf_path(file_name) # file_name为自定义语料库的路径
    • 关键词提取所使用停止词(Stop Words)文本语料库可以切换成自定义语料库的路径
      • 用法: jieba.analyse.set_stop_words(file_name) # file_name为自定义语料库的路径
      • 自定义语料库示例见这里
      • 用法示例见这里
  • 关键词一并返回关键词权重值示例

基于 TextRank 算法的关键词抽取

  • jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=(‘ns’, ‘n’, ‘vn’, ‘v’)) 直接使用,接口相同,注意默认过滤词性。
  • jieba.analyse.TextRank() 新建自定义 TextRank 实例

算法论文: TextRank: Bringing Order into Texts

基本思想:

  • 将待抽取关键词的文本进行分词
  • 以固定窗口大小(默认为5,通过span属性调整),词之间的共现关系,构建图
  • 计算图中节点的PageRank,注意是无向带权图

In [8]:

1
2
3
4
5
import jieba.analyse as analyse
lines = open('NBA.txt').read()
print " ".join(analyse.textrank(lines, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')))
print "---------------------我是分割线----------------"
print " ".join(analyse.textrank(lines, topK=20, withWeight=False, allowPOS=('ns', 'n')))
1
2
3
全明星赛  勇士  正赛  指导  对方  投篮  球员  没有  出现  时间  威少  认为  看来  结果  相隔  助攻  现场  三连庄  介绍  嘉宾
---------------------我是分割线----------------
勇士 正赛 全明星赛 指导 投篮 玩命 时间 对方 现场 结果 球员 嘉宾 时候 全队 主持人 特点 大伙 肥皂剧 全程 快船队

In [9]:

1
2
lines = open(u'西游记.txt').read()
print " ".join(analyse.textrank(lines, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')))
1
行者  师父  八戒  三藏  大圣  不知  菩萨  妖精  只见  长老  国王  却说  呆子  徒弟  小妖  出来  不得  不见  不能  师徒

词性标注

  • jieba.posseg.POSTokenizer(tokenizer=None) 新建自定义分词器,tokenizer 参数可指定内部使用的 jieba.Tokenizer 分词器。jieba.posseg.dt 为默认词性标注分词器。
  • 标注句子分词后每个词的词性,采用和 ictclas 兼容的标记法。
  • 具体的词性对照表参见计算所汉语词性标记集

In [10]:

1
2
3
4
import jieba.posseg as pseg
words = pseg.cut("我爱自然语言处理")
for word, flag in words:
print('%s %s' % (word, flag))
1
2
3
4
我 r
爱 v
自然语言 l
处理 v

并行分词

原理:将目标文本按行分隔后,把各行文本分配到多个 Python 进程并行分词,然后归并结果,从而获得分词速度的可观提升 基于 python 自带的 multiprocessing 模块,目前暂不支持 Windows

用法:

1
2
jieba.enable_parallel(4) # 开启并行分词模式,参数为并行进程数
jieba.disable_parallel() # 关闭并行分词模式

实验结果:在 4 核 3.4GHz Linux 机器上,对金庸全集进行精确分词,获得了 1MB/s 的速度,是单进程版的 3.3 倍。

注意:并行分词仅支持默认分词器 jieba.dt 和 jieba.posseg.dt。

In [11]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import sys
import time
import jieba

jieba.enable_parallel()
content = open(u'西游记.txt',"r").read()
t1 = time.time()
words = "/ ".join(jieba.cut(content))
t2 = time.time()
tm_cost = t2-t1
print('并行分词速度为 %s bytes/second' % (len(content)/tm_cost))

jieba.disable_parallel()
content = open(u'西游记.txt',"r").read()
t1 = time.time()
words = "/ ".join(jieba.cut(content))
t2 = time.time()
tm_cost = t2-t1
print('非并行分词速度为 %s bytes/second' % (len(content)/tm_cost))
1
2
并行分词速度为 830619.50933 bytes/second
非并行分词速度为 259941.448353 bytes/second

Tokenize:返回词语在原文的起止位置

注意,输入参数只接受 unicode

In [12]:

1
2
3
4
5
6
7
8
9
10
11
print "这是默认模式的tokenize"
result = jieba.tokenize(u'自然语言处理非常有用')
for tk in result:
print("%s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2]))

print "\n-----------我是神奇的分割线------------\n"

print "这是搜索模式的tokenize"
result = jieba.tokenize(u'自然语言处理非常有用', mode='search')
for tk in result:
print("%s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2]))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这是默认模式的tokenize
自然语言 start: 0 end:4
处理 start: 4 end:6
非常 start: 6 end:8
有用 start: 8 end:10

-----------我是神奇的分割线------------

这是搜索模式的tokenize
自然 start: 0 end:2
语言 start: 2 end:4
自然语言 start: 0 end:4
处理 start: 4 end:6
非常 start: 6 end:8
有用 start: 8 end:10

ChineseAnalyzer for Whoosh 搜索引擎

  • from jieba.analyse import ChineseAnalyzer

In [16]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals
import sys,os
sys.path.append("../")
from whoosh.index import create_in,open_dir
from whoosh.fields import *
from whoosh.qparser import QueryParser

analyzer = jieba.analyse.ChineseAnalyzer()
schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT(stored=True, analyzer=analyzer))

if not os.path.exists("tmp"):
os.mkdir("tmp")

ix = create_in("tmp", schema) # for create new index
#ix = open_dir("tmp") # for read only
writer = ix.writer()

writer.add_document(
title="document1",
path="/a",
content="This is the first document we’ve added!"
)

writer.add_document(
title="document2",
path="/b",
content="The second one 你 中文测试中文 is even more interesting! 吃水果"
)

writer.add_document(
title="document3",
path="/c",
content="买水果然后来世博园。"
)

writer.add_document(
title="document4",
path="/c",
content="工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作"
)

writer.add_document(
title="document4",
path="/c",
content="咱俩交换一下吧。"
)

writer.commit()
searcher = ix.searcher()
parser = QueryParser("content", schema=ix.schema)

for keyword in ("水果世博园","你","first","中文","交换机","交换"):
print(keyword+"的结果为如下:")
q = parser.parse(keyword)
results = searcher.search(q)
for hit in results:
print(hit.highlights("content"))
print("\n--------------我是神奇的分割线--------------\n")

for t in analyzer("我的好朋友是李明;我爱北京天安门;IBM和Microsoft; I have a dream. this is intetesting and interested me a lot"):
print(t.text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
水果世博园的结果为如下:
买<b class="match term0">水果</b>然后来<b class="match term1">世博园</b>

--------------我是神奇的分割线--------------

你的结果为如下:
second one <b class="match term0">你</b> 中文测试中文 is even more interesting

--------------我是神奇的分割线--------------

first的结果为如下:
<b class="match term0">first</b> document we’ve added

--------------我是神奇的分割线--------------

中文的结果为如下:
second one 你 <b class="match term0">中文</b>测试<b class="match term0">中文</b> is even more interesting

--------------我是神奇的分割线--------------

交换机的结果为如下:
干事每月经过下属科室都要亲口交代24口<b class="match term0">交换机</b>等技术性器件的安装工作

--------------我是神奇的分割线--------------

交换的结果为如下:
咱俩<b class="match term0">交换</b>一下吧
干事每月经过下属科室都要亲口交代24口<b class="match term0">交换</b>机等技术性器件的安装工作

--------------我是神奇的分割线--------------



朋友

李明


北京
天安
天安门
ibm
microsoft
dream
intetest
interest
me
lot

命令行分词

使用示例:python -m jieba news.txt > cut_result.txt

命令行选项(翻译):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
使用: python -m jieba [options] filename

结巴命令行界面。

固定参数:
filename 输入文件

可选参数:
-h, --help 显示此帮助信息并退出
-d [DELIM], --delimiter [DELIM]
使用 DELIM 分隔词语,而不是用默认的' / '
若不指定 DELIM,则使用一个空格分隔。
-p [DELIM], --pos [DELIM]
启用词性标注;如果指定 DELIM,词语和词性之间
用它分隔,否则用 _ 分隔
-D DICT, --dict DICT 使用 DICT 代替默认词典
-u USER_DICT, --user-dict USER_DICT
使用 USER_DICT 作为附加词典,与默认词典或自定义词典配合使用
-a, --cut-all 全模式分词(不支持词性标注)
-n, --no-hmm 不使用隐含马尔可夫模型
-q, --quiet 不输出载入信息到 STDERR
-V, --version 显示版本信息并退出

如果没有指定文件名,则使用标准输入。

--help 选项输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$> python -m jieba --help
Jieba command line interface.

positional arguments:
filename input file

optional arguments:
-h, --help show this help message and exit
-d [DELIM], --delimiter [DELIM]
use DELIM instead of ' / ' for word delimiter; or a
space if it is used without DELIM
-p [DELIM], --pos [DELIM]
enable POS tagging; if DELIM is specified, use DELIM
instead of '_' for POS delimiter
-D DICT, --dict DICT use DICT as dictionary
-u USER_DICT, --user-dict USER_DICT
use USER_DICT together with the default dictionary or
DICT (if specified)
-a, --cut-all full pattern cutting (ignored with POS tagging)
-n, --no-hmm don't use the Hidden Markov Model
-q, --quiet don't print loading messages to stderr
-V, --version show program's version number and exit

If no filename specified, use STDIN instead.

In [ ]:

1
 
|