DateWhale--2021.1--Task1

第一次参加DateWhale组织的组队学习,加入了数据分析的任务。本月的赛题是“学术前沿趋势分析”。
插一句:发现石墨文档的表单功能很不错。

Task1:论文数据统计。

  • 学习主题:论文数量统计,统计2019年全年,计算机各个方向的论文数量。
  • 涉及到的知识点:jupyter notebook中安装库;json文件的读取;列表推导式;爬虫;正则表达式

1、在jupyter notebook 中直接安装第三方库

在使用jupyter notebook的过程中,有时候会遇到需要安装第三方库的情况,可以直接在notebook中进行pip install,只需要前面加一个感叹号就行。

! pip install bs4

2、json文件的读取

首先导入json库

import json

使用with语句打开json文件,然后使用for循环读取数据。

data=[]

#使用with语句打开文件,有两个有点:1、自动关闭文件句柄。2、自动显示(处理)文件读取数据异常
with open('arxiv-metadata-oai-2019.json','r') as f:
    for idx,line in enumerate(f):
        #读取前100行,如果读取所有数据需要8GB内存
        if idx >=100:
            break
        data.append(json.loads(line))
        
data = pd.DataFrame(data) #将list变为dataframe格式,方便使用的pandas分析

json文件的格式如:
{"id":value, "name":value, "score":value}
{"id":value, "name":value, "score":value}
{"id":value, "name":value, "score":value}
将其转换为DataFrame格式之后,更便于使用Pandas进行分析。

可以定义一个专门用来读取json文件的函数。使用此函数进行读取时,可以指定读取的列,以及读取的行数。

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','categories','update_date'])
代码解析:
  • json.loads(str)的作用,就是把json的字符串转换成字典。效果如下,虽然转换前后看起来一样,本质上却不同。一个是字符串,一个是字典。
    json.load()
  • 在字典中筛选出指定的key,也可以使用字典推导式,用for语句,一句话完成:
d = {col:d[col] for col in columns}
  • json读取的基本的流程是:
    1、创建空列表data;
    2、循环data.append(字典),形成 [字典,字典,字典....];
    3、使用 pd.DataFrame(data),转化为DataFrame格式。

3、列表推导式

(字典也有推导式,如上文所述。)
列表推导式的执行是C语言的速度,比使用for循环更快。

对于复杂的双层循环,其执行逻辑如图:


列表推导式.png

这条语句的使用场景:
我有苹果+橘子,他有橘子+梨+香蕉。。。统计全班一共有几种水果。

4、提取年份

pd.to_datetime() :直接把字符串转换为datetime格式
.dt.year :提取出“年”

data["year"] = pd.to_datetime(data["update_date"]).dt.year
 #将update_date从例如2019-02-20的str变为datetime格式,并提取出year

类似的还有:

    df['purchase_date'] = pd.to_datetime(df['purchase_date'])
    df['year'] = df['purchase_date'].dt.year
    df['weekofyear'] = df['purchase_date'].dt.weekofyear
    df['month'] = df['purchase_date'].dt.month
    df['dayofweek'] = df['purchase_date'].dt.dayofweek
    df['weekend'] = (df.purchase_date.dt.weekday >=5).astype(int)
    df['hour'] = df['purchase_date'].dt.hour

5、删除一列

del data["update_date"]

6、重新编号

data.reset_index(drop=True, inplace=True) #重新编号

7、爬虫

from bs4 import BeautifulSoup
import requests

#爬取所有的类别
website_url = requests.get('https://arxiv.org/category_taxonomy').text #获取网页的文本数据
soup = BeautifulSoup(website_url,'lxml') #爬取数据,这里使用lxml的解析器,加速
root = soup.find('div',{'id':'category_taxonomy_list'}) #找出 BeautifulSoup 对应的标签入口
tags = root.find_all(["h2","h3","h4","p"], recursive=True) #读取 tags

website_url = requests.get('网址').text
返回网页的html文本
soup = BeautifulSoup(website_url,'lxml')
把html文本解析为soup对象
root = soup.find('div',{'id':'category_taxonomy_list'})
找出对应的标签入口。我们想要的数据,都在id为'category_taxonomy_list'的div标签中。
tags = root.find_all(["h2","h3","h4","p"], recursive=True)
读取 tags,把root中["h2","h3","h4","p"]标签都找出来。

8、正则表达式

re.sub(pattern,repl,string,count=0,flag=0)
用来 替换 或 提取 字符串
pattern : 正则中的模式字符串。
repl : 替换的字符串,也可为一个函数。
string : 要被查找替换的原始字符串。
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
flags : 编译时用的匹配模式,数字形式。
其中pattern、repl、string为必选参数

例如(替换):
a = re.sub(r'hello', 'i love the', 'hello world')
print(a)

输出'i love the world'
hello world里面的hello被 i love the替换

例如(提取):
a = re.sub('(\d{4})-(\d{2})-(\d{2})', r'\2-\3-\1', '2018-06-07')
>>> a
'06-07-2018'

这里,\2 指代的是前面的第二个分组。
通过这种方式,使用re.sub就可以从字符串中提取出想要的数据。
比如,原始字符串 raw="张三(警察)",要从中提取出“警察”,就可以使用如下代码:

raw="张三(警察)"
job = re.sub(r"(.*)\((.*)\)",r"\2",raw)
>>> job
'警察'

这里,( )代表一个分组,里面写 (.*),代表对这个分组里的内容不做要求。
"\(" 和 "\)"就是转义符,表示这就是括号符号,没有特殊意义。

9、分组聚合 .agg

.groupby( ).agg( )
使用agg函数,可以对同一列数据做两次聚合。

例如1:单列单种聚合

df.groupby(['key1'])['data1'].min() ==
df.groupby(['key1'])['data1'].agg({'min'}) ==
df.groupby(['key1']).agg({'data1':'min'})
对data1列,取各组的最小值,名字还是data1(推荐使用)

例如2:单列多种聚合

df.groupby(['key1'])['data1'].agg({'min','max'})==
df.groupby(['key1']).agg({'data1':['min','max']})
(推荐使用)

例如3:多列多种聚合
DataFrame.png
grouped = df1.groupby(['sex','smoker'])
# sex有 F M 二值,smoker有 Y N 二值,故分成四组。
grouped.agg({'age':['sum','mean'],'weight':['min','max']})
不同列运用不同的聚合函数.png
例如4:自定义聚合函数
def Max_cut_Min(group):
    return group.max()-group.min()

grouped.agg(Max_cut_Min)
自定义聚合函数.png

另外,group by之后,可以直接describe()

grouped = df1.groupby(['sex','smoker'])
grouped.describe()
groupby之后describe.png

10、饼图爆炸

使用不同的爆炸程度,可以将占比较小的份额更加清除地展现。

fig = plt.figure(figsize=(15,12))
explode = (0, 0, 0, 0.2, 0.3, 0.3, 0.2, 0.1) 
plt.pie(_df["id"],  labels=_df["group_name"], autopct='%1.2f%%', startangle=160, explode=explode)
plt.tight_layout()
plt.show()
饼图爆炸.png

11、DataFrame筛选——query函数更简洁

[参考文章]https://www.cnblogs.com/traditional/p/11111327.html
比如以下的df
"""
name age height where
0 mashiro 17 161 樱花庄
1 satori 17 155 地灵殿
2 koishi 16 154 地灵殿
3 kurisu 18 172 石头门
4 nagsia 21 158 clannad
"""

  • 筛选age > 17的
    print(df.query("age > 17")) # 等价于df[df["age"] > 17]
  • 筛选where == '地灵殿'的
    print(df.query("where == '地灵殿'")) # 等价于df[df["where"] > "地灵殿"]
  • 筛选where == '地灵殿' 并且 age == 17的,# 等价于df[(df["where"] > "地灵殿") & (df["age"] == 17)]
    print(df.query("where == '地灵殿' and age == 17"))
  • 筛选出身高大于年龄的,当然这个肯定都满足,只是演示一下query支持的功能
    print(df.query("height > age")) # 等价于df[df["height"] >df["age"]]
  • 另外如果字段名含有空格怎么办?我们将name变成na me
    df = df.rename(columns={"name": "na me"})
    这个时候应该将na me使用``包起来,告诉pandas,``里面的内容是一个整体。否则的话,是不会正确解析的
    print(df.query("`na me` == 'satori'"))
  • 查找age位于[17, 21]当中、并且where位于["樱花庄", "clannad]大当中的记录
    print(df.query("age in [17, 21] and where in ['樱花庄', 'clannad']"))
  • 问题来了,如果我们外面有一个变量,我们怎么在query里面使用呢? 如果使用的话,那么应该使用@作为前缀,来标识这是一个外面存在的变量
place="地灵殿"
print(df.query("where == @place"))

12、pandas中的透视表——pivot函数

df.groupby(["year","category_name"]).count().reset_index().pivot(index="category_name", columns="year",values="id")

按照种类和年份分组,然后使用count()聚合,然后使用reset_index()重新索引,最后使用pivot函数,搞成透视表的格式。
.pivot( )函数三个变量:index是行,columns是列,values是值。
.pivot( )函数一般 用在groupby分组聚合之后,index和columns是用来groupby的特征。
而且分组聚合之后,还要重新reset_index(),变成正常的数字index。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容