Python 是当今人工智能和机器学习领域最流行的编程语言之一。Python 以其有用的库和包而著称,即使没有软件工程背景的人也能编程。
Python 拥有一组与 Python 语言一起分发的标准库,如 DateTime、math 或 Random。本文的目标是在 GitHub 的 Python 仓库中找到 10 个最有用的标准库。为实现我们的目标,我们研究了 GitHub 中不同的 Python 仓库,并收集了它们的旧库来回答这个问题。
为了开始我们的研究,首先,我们收集了 GitHub 中 5 个著名的 Python 资源库在过去一年的提交情况。然后,我们对这些仓库中的 Python 源文件进行解析,并收集其提交中使用的库。最后,我们将这些 GitHub 仓库提交中使用的 10 个最流行的 Python 标准库进行可视化。
有不同的方法可以访问 GitHub 仓库中的数据,例如 GitHub torrent、Git API 调用或 Google big query。但是,在本文中,我们想要尝试一个新的非常有用的 Python 包,名为Pydriller,它可以收集我们需要的数据。Pydriller 速度快,使用方便。我是在攻读博士学位的时候熟悉了这个有趣的包。你可以在这里查看 Pydriller 的文档。要从 Pydriller 开始,首先,我们安装包:
pip install pydriller
每次提交时,GitHub 中的一个或多个源文件都可以被修改。在 GitHub 这样的版本控制系统中,每一次提交都有一个文件,名为“diff”。它通过提交特定的提交来存储在源文件中应用的更改。在 GitHub 仓库的提交中查找库的方法之一是在“diff”文件中搜索正则表达式。
但在本文中,我们想尝试一些不同的方法。我们比较两个不同版本的源文件“提交之前”和“提交之后”应用提交,然后收集这两个文件在库名中的差异。
通过这种方法,我们就可以发现库在不同提交中的使用频率。好消息是,Pydriller 允许我们在应用提交之前和应用提交之后访问源文件的版本。下面是收集数据所需的代码:
#import libraries
import pydriller as pyd
from datetime import datetime
#period to collect data
dt1 = datetime(2019, 11, 1)
dt2 = datetime(2020, 11, 1)
#path of 5 Python repositories
path = ["https://github.com/django/django.git","https://github.com/pandas-dev/pandas.git",
"https://github.com/numpy/numpy","https://github.com/home-assistant/home-assistant.git",
"https://github.com/donnemartin/system-design-primer.git"]
#collecting a version of a source file before and after applying a commit
tf_source = pd.DataFrame(columns=['commit_ID', 'before_Commit', 'after_Commit'])
for commit in pyd.RepositoryMining(path_to_repo=path, since=dt1, to=dt2).traverse_commits():
for modified_file in commit.modifications:
if modified_file.filename.endswith(".py"):
tf_source = tf_source.append({'commit_ID': commit.hash,'before_Commit': modified_file.source_code_before,
'after_Commit': modified_file.source_code}, ignore_index=True)
用 Pydriller 在 GitHub 中收集 5 个著名的 Python 库。
我们在 GitHub 上收集了 5 个大的 Python 项目在去年的提交情况,其中有Django、Pandas、NumPy、Homeassistant、system-design-primer。“RepositoryMining”是 Pydriller 的主要 API 调用之一。
我们可以通过 RepositoryMining 中的两个参数来定义一个时间段来收集不同仓库中的提交:since
和to
。另外,我们考虑所有名称以“.py”结尾的源文件的提交,因为这些资源库中也有其他编程语言的源文件,但我们关注的是 Python 库。
我们收集了三个特征:commit.hash
、source_code_before
、source_code
。Pydriller 中的commit.hash
返回提交的id
,source_code_before
是应用提交前的源文件版本,source_code
则显示提交后的源文件内容。下面是我们收集的数据头:
tf_source.head()
到目前为止,我们已经收集了开始旅程所需的数据。在下一节中,我们将学习如何探索这些源文件中的库。
提取源代码中信息的方法之一是将它们转换成抽象语法树(Abstract Syntax Tree,AST)。然后,我们就可以遍历这棵树,并收集目标节点。
但最重要的一点是,我们只想收集 Python 标准库,而不是所有在仓库中使用的包,比如本地定义的库,它们只有在仓库中才有意义。Python 标准库是和 Python 语言一起发布的。
因此,为了将标准包和其他包分开,我们需要拉取 Python 中所有有效的标准库。然后,我们可以写一个函数来收集源代码中的库名。
我们可以将本节分为两步:收集 Python 中所有可用的标准库的列表、构建基于抽象语法书的函数来收集库名。
在Python 的网站上,有一张 Python 中所有标准库的列表,并附有简短说明。这个页面将所有的 Python 标准库按字母名称排序,帮助我们对 Python 中所有的标准库进行拉取。我把所有 Python 标准库的列表放在这里,以 .csv 的格式。
既然我们有了所有标准 Python 库的列表,我们就需要从 Python GitHub 仓库中收集我们示例数据集中的库名称。正如我们提到的,其中一种方法是遍历抽象语法树。
在本文中,我们的目标节点是import
和importfrom
。我们希望有一个函数遍历解析树,找到目标节点,并返回库的名称。下面是这样做的类。
#import libraries
import ast
import tokenize
#A class to walk trough AST and collect libraries name
class FuncParser(ast.NodeVisitor):
def visit_Import(self, node):
tempImpo = node.names
if(tempImpo != None):
listImpo = tempImpo[0]
Impo = listImpo.name
if (Impo in api_name):
file_contents.append(Impo)
ast.NodeVisitor.generic_visit(self, node)
else:
ast.NodeVisitor.generic_visit(self, node)
def visit_ImportFrom(self, node):
module=node.module
if(module in api_name):
file_contents.append(module)
else:
ast.NodeVisitor.generic_visit(self, node)
def generic_visit(self, node):
ast.NodeVisitor.generic_visit(self, node)
在 Python 代码中收集库名的类。
为了更好地理解这个类的工作原理,下面是一段简单的代码。这段示例代码只有两行,分别导入了两个不同的库,其中一个是 python 标准库:tokenize
,另一个是本地库:assistant
。
import tokenize as tz
import assistant as ass
下面是这个示例代码的解析树的转储。可以发现,我们需要收集的是作为alias
类中的name
参数。
此外,我们还需要检查库的名称是否在我们从 Python 原始网站收集的所有标准库的列表中。我们将 .csv 文件保存在名为api_name
的列表中。
如果我们在这个示例代码上应用这个类FuncParser
,它将只返回tokenize
,因为另一个库assistant
在 Python 标准库列表中不可用。
Module(body=[Import(names=[alias(name='tokenize', asname='tz')]), Import(names=[alias(name='assistant', asname='ass')])])
到目前为止,我们收集了 GitHub 中 5 个著名的 Python 仓库的示例数据集,并构建了一个类来收集 Python 代码中的库名。
现在,我们需要将这个函数应用到 GitHub 的示例数据中,并找出这些仓库的提交中使用的前 10 个库。正如我们前面所讨论的,我们将提交提交之前的源文件的抽象语法树和提交提交之后的同一源文件的抽象语法树进行比较,然后我们收集不同的库节点。
然后我们收集不同的库节点。首先,我会给大家展示一个步骤性的示例,告诉大家如何比较这两个抽象语法树,最后,我把所有的代码放在一起,以循环遍历整个数据集,并计算每个库的出现次数。
我们将示例数据集存储在tf_source
中,我选择这个数据集的第一行来解释整个过程。tf_source
'Commit_before'返回示例数据集中第一次提交前的代码内容。
然后,我们应用FuncParser()
来收集这个源文件中的所有库名,并在file_contents
列表中返回结果。我们创建一个名为tokens_before
的数据框架,并存储这个列表。
text_before=str(tf_source[‘Commit_before’](0))
bf_obj = FuncParser()
bf_tree = ast.parse(text_before)
file_contents = []
bf_obj.visit(bf_tree)
dtobj_before = pd.DataFrame(file_contents, columns=[‘token’])
tokens_before =pd.DataFrame(dtobj_before[‘token’].value_counts())
我们重复与上面的步骤相同的过程,但这次是在提交提交之后对源文件的内容进行的,tf_source
‘Commit_after’。
另外,我们将结果存储在一个名为tokens_after
的数据帧中。
text_after=str(tf_source[‘Commit_after’](0))
aft_obj = FuncParser()
aft_tree = ast.parse(text_after)
file_contents = []
aft_obj.visit(aft_tree)
dtobj_after = pd.DataFrame(file_contents, columns=[‘token’])
tokens_after =pd.DataFrame(dtobj_after[‘token’].value_counts())
在这一步中,我们从tokens_after
中减去tokens_before
以计算它们的差异。
diff = tokens_after.subtract(tokens_before)
diff_token = diff[(diff.select_dtypes(include=[‘number’]) != 0).any(1)]
diff_token=diff_token.fillna(0)
diff_token= diff_token.abs()
diff_token = diff_token.reset_index()
最后,我们统计每个库在diff_token
数据帧中出现的次数。为此,我们创建一个名为py_lib
的字典,并统计库的出现次数。
py_lib={}
j=0
for j in range(0,len(diff_token)):
word = diff_token['index'](j).lower()
if word in py_lib:
py_lib[word]+=diff_token['token'](j)
else:
py_lib[word]=1
j+=
为了将上述步骤应用于我们在前面收集的整个示例数据中,我在步骤的开头添加了一个循环。下面是代码:
i=0
error=0
py_lib={}
for row in tf_source.iterrows():
#parsing the source file before applying commit i
if tf_source['Commit_before'](i) is not None:
try:
text_before=str(tf_source['Commit_before'](i))
bf_obj = FuncParser()
bf_tree = ast.parse(text_before)
file_contents = []
bf_obj.visit(bf_tree)
dtobj_before = pd.DataFrame(file_contents, columns=['token'])
tokens_before =pd.DataFrame(dtobj_before['token'].value_counts())
except:
error +=1
else:
file_contents = []
dtobj_before = pd.DataFrame(file_contents, columns=['token'])
tokens_before =pd.DataFrame(dtobj_before['token'].value_counts())
#parsing the source file after applying commit i
if tf_source['Commit_after'](i) is not None:
try:
text_after=str(tf_source['Commit_after'](i))
aft_obj = FuncParser()
aft_tree = ast.parse(text_after)
file_contents = []
aft_obj.visit(aft_tree)
dtobj_after = pd.DataFrame(file_contents, columns=['token'])
tokens_after =pd.DataFrame(dtobj_after['token'].value_counts())
except:
error +=1
else:
file_contents = []
dtobj_after = pd.DataFrame(file_contents, columns=['token'])
tokens_after =pd.DataFrame(dtobj_after['token'].value_counts())
#calculating the differences between two list tokens_before and tokens_after
diff = tokens_after.subtract(tokens_before)
diff_token = diff[(diff.select_dtypes(include=['number']) != 0).any(1)]
diff_token=diff_token.fillna(0)
diff_token= diff_token.abs()
diff_token = diff_token.reset_index()
# counting the numer of each libraries which are added or removed by commit i
j=0
for j in range(0,len(diff_token)):
word = diff_token['index'](j).lower()
if word in py_lib:
py_lib[word]+=diff_token['token'](j)
else:
py_lib[word]=1
j+=1
i+=1
在整个示例数据集中收集库。
现在我们收集了 GitHub 中所有 Python 仓库的库及其提交频率,我们想在py_lib
字典中找到前 10 个库。我们可以用下面的代码将前 10 个库的值收集到一个字典中。
我们可以看到,从示例数据集来看,warnings
、sys
或datetime
等库都在 Python 标准库的前 10 名列表中。
from operator import itemgetter
d=sorted(py_lib.items(), key=itemgetter(1),reverse=True)[:10]
[('warnings', 96.0),
('sys', 73.0),
('datetime', 28.0),
('test', 27.0),
('os', 22.0),
('collections', 18.0),
('io', 16.0),
('gc', 10.0),
('functools', 9.0),
('threading', 7.0)]
基于 GitHub 示例数据集的 Python 十大标准库。
另外,我们还可以绘制 Python 库的词云图及其频率。
import matplotlib.pyplot as plt
from wordcloud import WordCloud
wordcloud = WordCloud(background_color='black',max_font_size = 50)
wordcloud.generate_from_frequencies(frequencies=py_lib)
plt.figure(figsize=(8,6))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.show()
基于 GitHub 示例数据集的流行 Python 库的词云图。
在本文中,我们尝试基于一个示例数据集收集 10 个最受欢迎的 Python 库。这个数据集包含了 GitHub 中 5 个著名的 Python 仓库最近一年的提交情况。
我们使用 Pydriller 来收集 GitHub 的数据。我们对提交之前和提交之后的源文件抽象语法树进行了比较,并收集了这些提交中使用的库列表。最后,我们在词云图中绘制了最流行的 Python 库。
注:要复制本文的所有代码,可以在GitHub上找到。
作者介绍:
Arghavan Moradi,博士研究生,热爱学习,喜欢分享。
原文链接:
领取专属 10元无门槛券
私享最新 技术干货