学术论文趋势分析-论文代码统计Task3

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 正则表达式的使用方法来啦:

  1. 先进行编译工作: pattern样式变量 = re.compile(“要进行查找的格式”,可选参数flag = 匹配模式设置 )

  2. 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 数据中, 逐一判定是否为空。

所以此方法在这里行不通
那么我们再换一条思路,通过阅读下面文章:

补充:判断列表为空的操作

Python 判断一个列表是否为空列表

转换样式

判断完成之后,接下来我们就是将格式进行转换
查看一下样式:
在这里插入图片描述
查看数据类型:

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')

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值