1. 任务说明
任务主题:论⽂文代码统计,统计所有论⽂文出现代码的相关统计;
任务内容:使⽤用正则表达式统计代码连接、⻚数和图表数据;
任务成果:学习正则表达式统计
2. 分析说明
使⽤用正则表达式统计代码连接:
问题1:
什么是代码连接?长什么样子?在哪个变量中存储?
还有:⻚数统计,图表信息统计。
本节将对正则表达式对正则表达式进行学习。
3. 数据处理步骤
在原始 arxiv 数据集中作者经常会在论文的 comments 或 abstract 字段中给出具体的代码链接,所以我们需要从这些字段里面找出代码的链接。
确定数据出现的位置;
使用正则表达式完成匹配;
完成相关的统计;
4. 读取数据
# 导入所需的package
import seaborn as sns #用于画图
from bs4 import BeautifulSoup #用于爬取arxiv的数据
import re #用于正则表达式,匹配字符串的模式
import requests #用于网络连接,发送网络请求,使用域名获取对应信息
import json #读取数据,我们的数据为json格式的
import pandas as pd #数据处理,数据分析
import matplotlib.pyplot as plt #画图工具
def readArxivFile(path, columns=['id', 'submitter', 'authors', 'title', 'comments', 'journal-ref', 'doi',
'report-no', 'categories', 'license', 'abstract', 'versions',
'update_date', 'authors_parsed'], count=None):
'''
定义读取文件的函数
path: 文件路径
columns: 需要选择的列
count: 读取行数
'''
data = []
with open(path, 'r') as f:
for idx, line in enumerate(f):
if idx == count:
break
d = json.loads(line)
d = {col : d[col] for col in columns}
data.append(d)
data = pd.DataFrame(data)
return data
data = readArxivFile('arxiv-metadata-oai-2019.json', ['id', 'abstract', 'categories', 'comments'])
这里提取数据的过程和前面两个Task是一样的
5. 明确要提取的变量和变量形式
首先 commments 里面存储的是:
那么任务也就是说:从文本数据中提起符合格式的字符文本,并对这些文本进行统计。
comments里面用红笔标出的部分,找出同一个类别中的相同样式,比如:
pages类别的样式: 数字 + 空格 + pages
figures类别样式 : 数字 + 空格 + figures
tables类别样式: 数字 + 空格 + tables
虽然这3个类别不一样,但是通过此分析,我们发现它们的形式是一样的: 数字 + 空格 + 类别名
那么我们在提取的过程中所用的思路也是一样的
6. 任务指标的统计
数字 + 空格 + 类别名 样式统计
6.1 pages 对页数处理
# 使用正则表达式匹配,XX pages
data['pages'] = data['comments'].apply(lambda x: re.findall('[1-9][0-9]* pages', str(x)))
apply函数的用法(https://blog.csdn.net/starmoth/article/details/86323026)
lambda匿名函数用法(https://blog.csdn.net/qsx123432/article/details/111149469)
re.findall函数的使用方法(正则表达式re.compile()的使用)(python re 模块 findall 函数用法简述)
下面对上述的文章进行总结
apply() 函数的应用
使用场景:
DataFrame 的行迭代或者列迭代,
在某一个方向上对数据使用某个函数: Apply a function along an axis of the DataFrame.
使用方法:
变量名.apply(要使用的函数名称func,等参数)
data.apply(func, axis=0, raw=False, result_type=None, args=(), **kwds)
lambda() 函数
使用场景:让代码简单易读的场合,常配合 apply() 函数使用。
举例:
变量名.apply(lambda 形式参数 : 形式参数的函数表达式)
比如:
df_demo.apply(lambda x : x.mean() , axis = 1 )
re.compile() 和 re.findall()
compile = 编译
findall = find all = 寻找全部
那么针对 re 正则表达式的使用方法来啦:
-
先进行编译工作: pattern样式变量 = re.compile(“要进行查找的格式”,可选参数flag = 匹配模式设置 )
-
Pattern 对象文本匹配查找方法:
match 方法 search 方法 findall 方法 finditer 方法 split 方法 sub 方法 subn 方法
使用方法:
pattern样本变量.findall(待查找的文本变量)
具体案例:
import re
def main():
content = "hello I am jerry, from chongqing"
regex = re.compile('\w*o\w*') # 用编译设置正则表达的pattern样本格式
x = regex.findall(content) # 完全查找符合pattern样本格式的内容
print(x)
if __name__ == '__main__':
main()
['hello', 'from', 'chongqing']
在实验的过程中发现有个错误:
invalid character in identifier = 无效字符
解决办法: 查看自己的输入是否有中文输入下的字符,包括空格。然后在检查一下程序的缩进格式是否满足英文意义下的字符缩进
正则表达式的匹配形式
参考文章:
常用的正则表达式匹配规则
正式查找实验
经过上面的这些知识点:可以理解:
# 使用正则表达式匹配,XX pages
data['pages'] = data['comments'].apply(lambda x: re.findall('[1-9][0-9]* pages', str(x)))
此时data 中 的内容为:
查看发现,data中的论文会出现没有页数的数据。那么我们要将这些没有页数的数据进行剔除。
那怎么剔除呢? = 怎么将空值剔除?
通过apply方法使用len() 函数剔除空值
选出含有pages的论文,通过计算字符长度的方法进行
# 筛选出有pages的论文
data = data[data['pages'].apply(len) > 0]
通过布尔类型数据的方法剔除空值
参考文章:python pandas消除空值和空格的混淆
因为布尔类型可以只有两个值 True 和 False , 如果内容为空,那么这种类型的数据的返回值为False, 正式通过这种方法进行剔除
具体操作
None_vin = (data["pages"].isnull()) | (data["pages"].apply(lambda x: str(x).isspace()))
data[~None_vin].head()
运行结果发现没有将空列表剔除掉。那是为什么呢?
data["pages"].isnull()
发现没有空值。
# data["pages"].isnull()
data["pages"].apply(lambda x: str(x).isspace())
发现没有空格。
那么是怎么回事呢??
首先明白 空列表 ≠ 空格
下面将通过两个例子进行说明:
# DataFrame 类型数据
a = pd.DataFrame([[],
[1, 2, 3]])
print(a.isnull())
print(str(a).isspace())
# Series类型数据
b = pd.Series([[],
[1, 2, 3]])
print(b.isnull())
print(str(b).isspace())
运行结果如下:
从这里看出来,Series数据中,[] ≠ 空值。
DataFrame 数据中, 逐一判定是否为空。
便可以进行总结了:
对于 isspace() 而言:
空格 = 1个字符长度 ≠ 空列表 =长度为0
对于 isnull() 而言:
Series数据中,[] ≠ 空值。
DataFrame 数据中, 逐一判定是否为空。
所以此方法在这里行不通
那么我们再换一条思路,通过阅读下面文章:
补充:判断列表为空的操作
转换样式
判断完成之后,接下来我们就是将格式进行转换
查看一下样式:
查看数据类型:
print(type(data["pages"]))
print(type(data["pages"][0]))
print(type(data["pages"][0][0]))
<class 'pandas.core.series.Series'>
<class 'list'>
<class 'str'>
那么接下来的任务就是将字符串列表转换成数值型列表
使用replace() 函数将现有格式转换成要求格式
replace() 函数的使用方法:
变量名.replace("待替换的内容", "要重新填写的内容" , 替换次数)
就是用后一个内容去替换掉前一个内容,并且替换次数可以给出。
举例:
my_str = "lowmanmana"
new_str = my_str.replace("m", "h", 3)
print(new_str)
lowhanhana
基于这种思路上去替换掉内容。
1.先取出 data["pages"]
2.然后应用 apply() 函数,里面写替换函数的使用操作
3.因为data["pages"]是一个Series数据,里面的元素是列表,因此要对列表中的元素进行操作,所以要用 index = 0将列表中的字符串取出
4.在取完后将其进行替换掉,然后再将其转化成 数值型
从上面的步骤中可以进行如下操作:
1. data["pages"]
2. data["pages"].apply(lambda x : x[0].replace(" pages", "")) # 此处是string类型
3. data["pages"].apply(lambda x : float(x[0].replace(" pages", ""))) # 此处是 float类型
具体实施:
# 由于匹配得到的是一个list,如['19 pages'],需要进行转换
data['pages'] = data['pages'].apply(lambda x: float(x[0].replace(' pages', '')))
将已经转换的数据进行统计
data['pages'].describe().astype(int)
count 80696
mean 18
std 20
min 1
25% 9
50% 14
75% 24
max 1958
Name: pages, dtype: int32
对pages进行可视化处理
按照主要的类别可视化
# 选择主要类别
data['categories'] = data['categories'].apply(lambda x: x.split(' ')[0])
data['categories'] = data['categories'].apply(lambda x: x.split('.')[0])
data["categories"]
然后根据类别,对pages使用统计函数,然后进行可视化
从上面的文字中可以看出,要使用groupby() 方法。
怎么看出来的呢?(按……进行分组,然后对……进行操作。这个大概率使用到 groupby() 方法)
# 每类论文的平均页数
plt.figure(figsize=(12, 6))
data.groupby(['categories'])['pages'].mean().plot(kind='bar')
6.2 figures 对图处理
思路和 pages 的思路是一样的
data['figures'] = data['comments'].apply(lambda x: re.findall('[1-9][0-9]* figures', str(x)))
data = data[data['figures'].apply(len) > 0]
data['figures'] = data['figures'].apply(lambda x: float(x[0].replace(' figures', '')))
6.3 对代码链接的处理
# 筛选包含github的论文
data_with_code = data[
(data.comments.str.contains('github')==True)|
(data.abstract.str.contains('github')==True)
]
data_with_code['text'] = data_with_code['abstract'].fillna('') + data_with_code['comments'].fillna('')
# 使用正则表达式匹配论文
pattern = '[a-zA-z]+://github[^\s]*'
# data_with_code['code_flag'] = data_with_code['text'].str.findall(pattern).apply(len)
'''
这里想表达的意思是不是找出含有链接的数据?如果comments和abstract里都有链接或者有多个链接,那code_flag就会大于1,这样筛出来的数据就有问题了。所有我认为这里apply和data_with_code['code_flag']==1至少需要修改其一
修改之后为:下面的代码:
'''
data_with_code['code_flag'] = data_with_code['text'].str.findall(pattern).apply(lambda x : 0 if len(x) <1 else 1)
下面对代码进行理解:
我们的目的是要找出包含规定代码链接的论文,那么我们要从哪里找出来呢?
经过查看数据可知,在 abstract 和 comments 中的会存在代码链接
那么我们就应该从这里面进行选择。
首先将这两个类别下的数据进行一个简单的取出
print(data["comments"].head())
print(type(data["comments"]))
print("*"*66)
print(data["abstract"].head())
print(type(data["abstract"]))
0 15 pages, 15 figures, 3 tables, submitted to M...
1 27 pages
2 6 pages, 3 figures, accepted in A&A
4 32 pages (referee format), 9 figures, ApJ acce...
5 8 pages, 13 figures
Name: comments, dtype: object
<class 'pandas.core.series.Series'>
******************************************************************
0 We systematically explore the evolution of t...
1 Cofibrations are defined in the category of ...
2 We explore the effect of an inhomogeneous ma...
4 The most massive elliptical galaxies show a ...
5 Differential and total cross-sections for ph...
Name: abstract, dtype: object
<class 'pandas.core.series.Series'>
然后再用字符的形式将其取出。
print(data["comments"].str) # 等于 # data.comments.str
print(data["abstract"].str) # 等于 # data.abstract.str
print()
print(str(data["comments"]))
print(str(data["abstract"]))
<pandas.core.strings.StringMethods object at 0x000001FEF64BF190>
<pandas.core.strings.StringMethods object at 0x000001FEF438FDC0>
0 15 pages, 15 figures, 3 tables, submitted to M...
1 27 pages
2 6 pages, 3 figures, accepted in A&A
4 32 pages (referee format), 9 figures, ApJ acce...
5 8 pages, 13 figures
...
170611 5 pages LaTeX
170612 Revtex 6 pages, 3 postscript figures, minor ty...
170615 plain LaTeX, 28 pages
170616 42 pages
170617 13 pages, latex, no figures
Name: comments, Length: 80696, dtype: object
0 We systematically explore the evolution of t...
1 Cofibrations are defined in the category of ...
2 We explore the effect of an inhomogeneous ma...
4 The most massive elliptical galaxies show a ...
5 Differential and total cross-sections for ph...
...
170611 Without imposing the locality condition,it i...
170612 A quantum computer is proposed in which info...
170615 We consider a hierarchy of many-particle sys...
170616 Consider the evolution $$ \frac{\pl m_\iy}{\...
170617 A general solution to the Complex Monge-Amp\...
Name: abstract, Length: 80696, dtype: object
str() 和 .str的区别
从上面的结果来看,我们又发现了另一个问题: str() 函数 和 .str属性有什么不一样的。
str()
.str
从这里看出来, 它们俩是 instance 和 class 的区别
那么接下来先看一下 instance 和 class 的区别。
instance = 实例
class = 类
两者的区别参考文章:
Python学习:类和实例
编程中实例是什么?什么是实例?实例化又是什么?什么是类?什么是对象?
而在这里
data["comments"].str 是指 单独把数据转换成 str形式
str(data["comments"]) 是将这个数据变量变成 str
fillna() 方法
参考文章:
Python-pandas的fillna()方法-填充空值
这里使用了 fillna() 方法。下面对其进行简要总结:
pandas.Series.fillna(value = 用什么值进行填充, method = 填充方式, axis = 填充的轴向, limit = 填充多少个 )
pandas.DateFrame.fillna(value = 用什么值进行填充, method = 填充方式, axis = 填充的轴向, limit = 填充多少个 )
7. 可视化处理
data_with_code = data_with_code[data_with_code['code_flag'] == 1]
plt.figure(figsize=(12, 6))
data_with_code.groupby(['categories'])['code_flag'].count().plot(kind='bar')