整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

前端:JS延长加载的方式有哪些?

么是JS延迟加载?

JS延迟加载,也就是等页面加载完成之后再加载JavaScript文件

为什么让JS实现延迟加载?

js的延迟加载有助于提高页面的加载速度。

Js延迟加载的方式有哪些?一般有以下几种方式:

·defer属性

·async属性

·动态创建DOM方式

·使用jQuery的getScript方法

·使用setTimeout延迟方法

·让JS最后加载

1、defer属性

HTML 4.01为<script>标签定义了defer属性。标签定义了defer属性元素中设置defer属性,等于告诉浏览器立即下载,但延迟执行标签定义了defer属性。

用途:表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕之后再执行在<script>元素中设置defer属性,等于告诉浏览器立即下载,但延迟执行

<!DOCTYPE html>
<html>
<head>
	<script src="test1.js" defer="defer"></script>
	<script src="test2.js" defer="defer"></script>
</head>
<body>
<!--这里放内容-->
</body>
</html>

说明:虽然<script>元素放在了<head>元素中,但包含的脚本将延迟浏览器遇到</html>标签后再执行HTML5规范要求脚本按照它们出现的先后顺序执行。在现实当中,延迟脚本并不一定会按照顺序执行defer属性只适用于外部脚本文件。支持HTML5的实现会忽略嵌入脚本设置的defer属性

2、async属性

HTML5 为<script>标签定义了async属性。与defer属性类似,都用于改变处理脚本的行为。同样,只适用于外部脚本文件。标签定义了async属性。与defer属性类似,都用于改变处理脚本的行为。同样,只适用于外部脚本文件。

目的:不让页面等待脚本下载和执行,从而异步加载页面其他内容。异步脚本一定会在页面 load 事件前执行。不能保证脚本会按顺序执行

<!DOCTYPE html>
<html>
	<head>
		<script src="test1.js" async></script>
		<script src="test2.js" async></script>
	</head>
<body>
<!--这里放内容-->
</body>
</html>

async和defer一样,都不会阻塞其他资源下载,所以不会影响页面的加载。

缺点:不能控制加载的顺序

3、动态创建DOM方式

//这些代码应被放置在</ body>标签前(接近HTML文件底部)
<script type="text/javascript">
	function downloadJSAtOnload() {
		varelement = document .createElement("script");
		element.src = "defer.js";
		document.body.appendChild(element);
	}
	if (window. addEventListener)
		window.addEventListener("load" ,downloadJSAtOnload, false);
	else if (window.attachEvent)
		window.attachEvent("onload", downloadJSAtOnload) ;
	else
		window. onload =downloadJSAtOnload;
</script>

4、使用jQuery的getScript()方法

$.getScript("outer.js" , function(){	//回调函数,成功获取文件后执行的函数
	console.log(“脚本加载完成")
});

5、使用setTimeout延迟方法的加载时间延迟加载js代码,给网页加载留出更多时间

<script type="text/javascript" >
	function A(){
		$.post("/1ord/1ogin" ,{name:username,pwd:password},function(){
			alert("Hello");
		});
	}
	$(function (){
		setTimeout('A()', 1000);	//延迟1秒
	})
</script>

6、让JS最后加载

把js外部引入的文件放到页面底部,来让js最后引入,从而加快页面加载速度例如引入外部js脚本文件时,如果放入html的head中,则页面加载前该js脚本就会被加载入页面,而放入body中,则会按照页面从上倒下的加载顺序来运行JavaScript的代码。所以我们可以把js外部引入的文件放到页面底部,来让js最后引入,从而加快页面加载速度。

上述方法2也会偶尔让你收到Google页面速度测试工具的“延迟加载javascript”警告。所以这里的解决方案将是来自Google帮助页面的推荐方案。

//这些代码应被放置在</body>标签前(接近HTML文件底部)

<script type= "text/javascript">
	function downloadJSAtonload() {
		var element = document.createElement("script");
		element.src = "defer.js";
		document.body.appendChild(element);
	}
	if (window.addEventListener)
		window.addEventListener("load", downloadJSAtOnload, false);
	else if (window.attachEvent )
		window.attachEvent("onload", downloadJSAtonload);
	else window.onload = downloadJSAtOnload;
</script>

这段代码意思等到整个文档加载完后,再加载外部文件“defer.js”。

使用此段代码的步骤:

6.1)复制上面代码

6.2)粘贴代码到HTML的标签前 (靠近HTML文件底部)

6.3)修改“defer.js”为你的外部JS文件名

6.4)确保文件路径是正确的。例如:如果你仅输入“defer.js”,那么“defer.js”文件一定与HTML文件在同一文件夹下。

注意:

这段代码直到文档加载完才会加载指定的外部js文件。因此,不应该把那些页面正常加载需要依赖的javascript代码放在这里。而应该将JavaScript代码分成两组。一组是因页面需要而立即加载的javascript代码,另外一组是在页面加载后进行操作的javascript代码(例如添加click事件。

x1 工具准备

工欲善其事必先利其器,爬取语料的根基便是基于python。

我们基于python3进行开发,主要使用以下几个模块:requests、lxml、json。

简单介绍一个各模块的功能

01|requests

requests是一个Python第三方库,处理URL资源特别方便。它的官方文档上写着大大口号:HTTP for Humans(为人类使用HTTP而生)。相比python自带的urllib使用体验,笔者认为requests的使用体验比urllib高了一个数量级。

我们简单的比较一下:

urllib:

 1import urllib2
 2import urllib
 3
 4URL_GET = "https://api.douban.com/v2/event/list"
 5#构建请求参数
 6params = urllib.urlencode({'loc':'108288','day_type':'weekend','type':'exhibition'})
 7
 8#发送请求
 9response = urllib2.urlopen('?'.join([URL_GET,'%s'])%params)
10#Response Headers
11print(response.info())
12#Response Code
13print(response.getcode())
14#Response Body
15print(response.read())
复制代码

requests:

 1import requests
 2
 3URL_GET = "https://api.douban.com/v2/event/list"
 4#构建请求参数
 5params = {'loc':'108288','day_type':'weekend','type':'exhibition'}
 6
 7#发送请求
 8response = requests.get(URL_GET,params=params)
 9#Response Headers
10print(response.headers)
11#Response Code
12print(response.status_code)
13#Response Body
14print(response.text)复制代码

我们可以发现,这两种库还是有一些区别的:

1. 参数的构建:urllib需要对参数进行urlencode编码处理,比较麻烦;requests无需额外编码处理,十分简洁。

2. 请求发送:urllib需要额外对url参数进行构造,变为符合要求的形式;requests则简明很多,直接get对应链接与参数。

3. 连接方式:看一下返回数据的头信息的“connection”,使用urllib库时,"connection":"close",说明每次请求结束关掉socket通道,而使用requests库使用了urllib3,多次请求重复使用一个socket,"connection":"keep-alive",说明多次请求使用一个连接,消耗更少的资源

4. 编码方式:requests库的编码方式Accept-Encoding更全,在此不做举例

综上所诉,使用requests更为简明、易懂,极大的方便我们开发。

02|lxml

BeautifulSoup是一个库,而XPath是一种技术,python中最常用的XPath库是lxml。

当我们拿到requests返回的页面后,我们怎么拿到想要的数据呢?这个时候祭出lxml这强大的HTML/XML解析工具。python从不缺解析库,那么我们为什么要在众多库里选择lxml呢?我们选择另一款出名的HTML解析库BeautifulSoup来进行对比。

我们简单的比较一下:

BeautifulSoup:

1from bs4 import BeautifulSoup #导入库
2# 假设html是需要被解析的html
3
4#将html传入BeautifulSoup 的构造方法,得到一个文档的对象
5soup = BeautifulSoup(html,'html.parser',from_encoding='utf-8')
6#查找所有的h4标签 
7links = soup.find_all("h4")
复制代码

lxml:

1from lxml import etree
2# 假设html是需要被解析的html
3
4#将html传入etree 的构造方法,得到一个文档的对象
5root = etree.HTML(html)
6#查找所有的h4标签 
7links = root.xpath("//h4")
复制代码

我们可以发现,这两种库还是有一些区别的:

1. 解析html: BeautifulSoup的解析方式和JQ的写法类似,API非常人性化,支持css选择器;lxml的语法有一定的学习成本

2. 性能:BeautifulSoup是基于DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多;而lxml只会局部遍历,另外lxml是用c写的,而BeautifulSoup是用python写的,明显的性能上lxml>>BeautifulSoup。

综上所诉,使用BeautifulSoup更为简明、易用,lxml虽然有一定学习成本,但总体也很简明易懂,最重要的是它基于C编写,速度快很多,对于笔者这种强迫症,自然而然就选lxml啦。

03|json

python自带json库,对于基础的json的处理,自带库完全足够。但是如果你想更偷懒,可以使用第三方json库,常见的有demjson、simplejson。

这两种库,无论是import模块速度,还是编码、解码速度,都是simplejson更胜一筹,再加上兼容性 simplejson 更好。所以大家如果想使用方库,可以使用simplejson。

0x2 确定语料源

将武器准备好之后,接下来就需要确定爬取方向。

以电竞类语料为例,现在我们要爬电竞类相关语料。大家熟悉的电竞平台有企鹅电竞、企鹅电竞和企鹅电竞(斜眼),所以我们以企鹅电竞上直播的游戏作为数据源进行爬取。

我们登陆企鹅电竞官网,进入游戏列表页,可以发现页面上有很多游戏,通过人工去写这些游戏名收益明显不高,于是我们就开始我们爬虫的第一步:游戏列表爬取。


 1import requests
 2from lxml import etree
 3
 4# 更新游戏列表
 5def _updateGameList():
 6 # 发送HTTP请求时的HEAD信息,用于伪装为浏览器
 7 heads = { 
 8 'Connection': 'Keep-Alive',
 9 'Accept': 'text/html, application/xhtml+xml, */*',
10 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
11 'Accept-Encoding': 'gzip, deflate',
12 'User-Agent': 'Mozilla/6.1 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
13 }
14 # 需要爬取的游戏列表页
15 url = 'https://egame.qq.com/gamelist'
16
17 # 不压缩html,最大链接时间为10妙
18 res = requests.get(url, headers=heads, verify=False, timeout=10)
19 # 为防止出错,编码utf-8
20 res.encoding = 'utf-8'
21 # 将html构建为Xpath模式
22 root = etree.HTML(res.content)
23 # 使用Xpath语法,获取游戏名
24 gameList = root.xpath("//ul[@class='livelist-mod']//li//p//text()")
25 # 输出爬到的游戏名
26 print(gameList)
复制代码

当我们拿到这几十个游戏名后,下一步就是对这几十款游戏进行语料爬取,这时候问题就来了,我们要从哪个网站来爬这几十个游戏的攻略呢,taptap?多玩?17173?在对这几个网站进行分析后,发现这些网站仅有一些热门游戏的文章语料,一些冷门或者低热度的游戏,例如“灵魂筹码”、“奇迹:觉醒”、“死神来了”等,很难在这些网站上找到大量文章语料,如图所示:

我们可以发现,“ 奇迹:觉醒”、“灵魂筹码”的文章语料特别少,数量上不符合我们的要求。 那么有没有一个比较通用的资源站,它拥有着无比丰富的文章语料,可以满足我们的需求。

其实静下心来想想,这个资源站我们天天都有用到,那就是百度。我们在百度新闻搜索相关游戏,拿到搜索结果列表,这些列表的链接的网页内容几乎都与搜索结果强相关,这样我们数据源不够丰富的问题便轻松解决了。但是此时出现了一个新的问题,并且是一个比较难解决的问题——如何抓取到任意网页的文章内容?

因为不同的网站都有不同的页面结构,我们无法与预知将会爬到哪个网站的数据,并且我们也不可能针对每一个网站都去写一套爬虫,那样的工作量简直难以想象!但是我们也不能简单粗暴的将页面中的所有文字都爬下来,用那样的语料来进行训练无疑是噩梦!

经过与各个网站斗智斗勇、查询资料与思索之后,终于找到一条比较通用的方案,下面为大家讲一讲笔者的思路。

0x3 任意网站的文章语料爬取

01|提取方法

1)基于Dom树正文提取

2)基于网页分割找正文块

3)基于标记窗的正文提取

4)基于数据挖掘或机器学习

5)基于行块分布函数正文提取

02|提取原理

大家看到这几种是不是都有点疑惑了,它们到底是怎么提取的呢?让笔者慢慢道来。

1)基于Dom树的正文提取:

这一种方法主要是通过比较规范的HTML建立Dom树,然后地柜遍历Dom,比较并识别各种非正文信息,包括广告、链接和非重要节点信息,将非正文信息抽离之后,余下来的自然就是正文信息。

但是这种方法有两个问题

① 特别依赖于HTML的良好结构,如果我们爬取到一个不按W3c规范的编写的网页时,这种方法便不是很适用。

② 树的建立和遍历时间复杂度、空间复杂度都较高,树的遍历方法也因HTML标签会有不同的差异。

2) 基于网页分割找正文块 :

这一种方法是利用HTML标签中的分割线以及一些视觉信息(如文字颜色、字体大小、文字信息等)。

这种方法存在一个问题:

① 不同的网站HTML风格迥异,分割没有办法统一,无法保证通用性。

3) 基于标记窗的正文提取:

先科普一个概念——标记窗,我们将两个标签以及其内部包含的文本合在一起成为一个标记窗(比如 <h1>我是h1</h1> 中的“我是h1”就是标记窗内容),取出标记窗的文字。

这种方法先取文章标题、HTML中所有的标记窗,在对其进行分词。然后计算标题的序列与标记窗文本序列的词语距离L,如果L小于一个阈值,则认为此标记窗内的文本是正文。

这种方法虽然看上去挺好,但其实也是存在问题的:

① 需要对页面中的所有文本进行分词,效率不高。

② 词语距离的阈值难以确定,不同的文章拥有不同的阈值。

4)基于数据挖掘或机器学习

使用大数据进行训练,让机器提取主文本。

这种方法肯定是极好的,但是它需要先有html与正文数据,然后进行训练。我们在此不进行探讨。

5)基于行块分布函数正文提取

对于任意一个网页,它的正文和标签总是杂糅在一起。此方法的核心有亮点:① 正文区的密度;② 行块的长度;一个网页的正文区域肯定是文字信息分布最密集的区域之一,这个区域可能最大(评论信息长、正文较短),所以同时引进行块长度进行判断。

实现思路:

① 我们先将HTML去标签,只留所有正文,同时留下标签取出后的所有空白位置信息,我们称其为Ctext;

② 对每一个Ctext取周围k行(k<5),合起来称为Cblock;

③ 对Cblock去掉所有空白符,其文字总长度称为Clen;

④ 以Ctext为横坐标轴,以各行的Clen为纵轴,建立坐标系。

以这个网页为例: http://www.gov.cn/ldhd/2009-11/08/content_1459564.htm 该网页的正文区域为145行至182行。


由上图可知,正确的文本区域全都是分布函数图上含有最值且连续的一个区域,这个区域往往含有一个骤升点和一个骤降点。因此,网页正文抽取问题转化为了求行块分布函数上的骤升点和骤降点两个边界点,这两个边界点所含的区域包含了当前网页的行块长度最大值并且是连续的。

经过大量实验,证明此方法对于中文网页的正文提取有较高的准确度,此算法的优点在于,行块函数不依赖与HTML代码,与HTML标签无关,实现简单,准确率较高。

主要逻辑代码如下:

 1# 假设content为已经拿到的html
 2
 3# Ctext取周围k行(k<5),定为3
 4blocksWidth = 3
 5# 每一个Cblock的长度
 6Ctext_len = []
 7# Ctext
 8lines = content.split('n')
 9# 去空格
10for i in range(len(lines)):
11 if lines[i] == ' ' or lines[i] == 'n':
12 lines[i] = ''
13# 计算纵坐标,每一个Ctext的长度
14for i in range(0, len(lines) - blocksWidth):
15 wordsNum = 0
16 for j in range(i, i + blocksWidth):
17 lines[j] = lines[j].replace("\s", "")
18 wordsNum += len(lines[j])
19 Ctext_len.append(wordsNum)
20# 开始标识
21start = -1
22# 结束标识
23end = -1
24# 是否开始标识
25boolstart = False
26# 是否结束标识
27boolend = False
28# 行块的长度阈值
29max_text_len = 88
30# 文章主内容
31main_text = []
32# 没有分割出Ctext
33if len(Ctext_len) < 3:
34 return '没有正文'
35for i in range(len(Ctext_len) - 3):
36 # 如果高于这个阈值
37 if(Ctext_len[i] > max_text_len and (not boolstart)):
38 # Cblock下面3个都不为0,认为是正文
39 if (Ctext_len[i + 1] != 0 or Ctext_len[i + 2] != 0 or Ctext_len[i + 3] != 0):
40 boolstart = True
41 start = i
42 continue
43 if (boolstart):
44 # Cblock下面3个中有0,则结束
45 if (Ctext_len[i] == 0 or Ctext_len[i + 1] == 0):
46 end = i
47 boolend = True
48 tmp = []
49
50 # 判断下面还有没有正文
51 if(boolend):
52 for ii in range(start, end + 1):
53 if(len(lines[ii]) < 5):
54 continue
55 tmp.append(lines[ii] + "n")
56 str = "".join(list(tmp))
57 # 去掉版权信息
58 if ("Copyright" in str or "版权所有" in str):
59 continue
60 main_text.append(str)
61 boolstart = boolend = False
62# 返回主内容
63result = "".join(list(main_text))
复制代码

0x4 结语

至此我们就可以获取任意内容的文章语料了,但这仅仅是开始,获取到了这些语料后我们还需要在一次进行清洗、分词、词性标注等,才能获得真正可以使用的语料。

本篇文章主要介绍了pandas中对series和dataframe对象进行连接的方法:pd.append()和pd.concat(),文中通过示例代码对这两种方法进行了详细的介绍,希望能对各位python小白的学习有所帮助。

一、df.append(df)

描述:append方法用以在表尾中添加新的行,并返回追加后的数据对象,若追加的行中存在原数据没有的列,会新增一列,并用nan填充;若追加的行数据中缺少原数据某列,同样以nan填充

语法:df.append(other, ignore_index=False, verify_integrity=False, sort=None)

​ 参数说明:

  • other:要追加的数据,可以是dataframe,series,字典,列表
  • ignore_index:两个表的index是否有实际含义,默认为False,若ignore_index=True,表根据列名对齐合并,生成新的index
  • verify_integrity:默认为False,若为True,创建具有重复项的索引时引发ValueError
  • sort:默认为False,若为True如果’ self ‘和’ other '的列没有对齐,则对列进行排序。

下面对append方法的每个参数进行详细介绍:

​ 第一个参数为other:要追加的数据,可以是dataframe,series,字典,列表甚至是元素;但前后类型要一致。

  1. 将数据追加到series
# 将数据追加到series
<<< a=df.iloc[0,:]
<<< b=df.iloc[6,:]
<<< a.append(b)      #需赋给新值,不改变原数组
A     0
B     1
C     2
D     3
E     4
F     5
A    36
B    37
C    38
D    39
E    40
F    41
dtype: int32
<<< a
A    0
B    1
C    2
D    3
E    4
F    5
Name: S1, dtype: int32

<<< c=a.append(b)    # 保存为c
<<< c
A     0
B     1
C     2
D     3
E     4
F     5
A    36
B    37
C    38
D    39
E    40
F    41
dtype: int32
  1. 将数据追加到dataframe
# 将数据追加到dataframe
<<< a=df.iloc[0:2,:]
<<< b=df.iloc[4:6,:] 
<<< c=a.append(b)           # 注意是纵向追加,不支持横向追加
<<< c
    A	B	C	D	E   F
S1	0	1	2	3	4	5
S2	6	7	8	9	10	11
S5	24	25	26	27	28	29
S6	30	31	32	33	34	35

​ 注意:获取单行得到的结果是一维数组,当一维数组[6,:]和二维数组[2,6]追加时,会得到8*7的数组,匹配不上的地方用NA填充。

# 将二维数组追加到一维数组
<<< a=df.iloc[0,:]
<<< b=df.iloc[4:6,:] 
<<< c=a.append(b)          
<<< c
      0	  A	  B	  C	  D	  E	  F
A	0.0	NaN	NaN	NaN	NaN	NaN	NaN
B	1.0	NaN	NaN	NaN	NaN	NaN	NaN
C	2.0	NaN	NaN	NaN	NaN	NaN	NaN
D	3.0	NaN	NaN	NaN	NaN	NaN	NaN
E	4.0	NaN	NaN	NaN	NaN	NaN	NaN
F	5.0	NaN	NaN	NaN	NaN	NaN	NaN
S5	NaN	24.0	25.0	26.0	27.0	28.0	29.0
S6	NaN	30.0	31.0	32.0	33.0	34.0	35.0
  1. 将数据追加到list
  • list是一维:以列的形式来进行追加操作
  • list是二维:以行的形式来进行追加操作
  • list是三维:只添加一个值注意:追加到列表时,是在原数组改动,是在原数组改动,是在原数组改动
# 列表追加到列表
<<< a=[]
<<< b=df.iloc[6,:].tolist()
<<< a.append(b)
<<< a
[[36, 37, 38, 39, 40, 41]]

# 序列追加到列表
<<< a=[1,2,3,4,5,6,7]
<<< b=df.iloc[6,:]
<<< a.append(b)
<<< a
[1, 2, 3, 4, 5, 6, 7, A    36
 B    37
 C    38
 D    39
 E    40
 F    41
 Name: S7, dtype: int32]
  1. 追加字典TypeError: Can only append a Series if ignore_index=True or if the Series has a name
<<< df1=pd.DataFrame()
<<< a={'A':1,'B':2}
<<< df1=df1.append(a,ignore_index=True)
<<< df1
    A	B
0	1	2
  1. 将单个元素追加到列表

​ append方法也可以将单个元素追加到列表(其他对象不行),会自动将单个元素转为列表对象,再进行追加操作

# 单个元素进行追加
<<< a=[1,2,3,4,5,6,7,8]
<<< a.append(9)
<<< a
[1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. 将其他类型对象追加到dataframe当dataframe使用append方法添加series或字典的时候,必须要设置name,设置name名称将会作为index的name,否则会报错提示:TypeError: Can only append a Series if ignore_index=True or if the Series has a name
<<< df1=pd.DataFrame()
<<< ser=pd.Series({"x":1,"y":2},name="a")
<<< df1=df1.append(ser)
<<< df1
    x   y
a	1	2

​ 如果不添加name,也可以添加参数ignore_index:

<<< df1=pd.DataFrame()
<<< ser=pd.Series({"x":1,"y":2})
<<< df1=df1.append(ser,ignore_index=True)
<<< df1
    x   y
a	1	2

​ 第二个参数:两个表的index是否有实际含义,默认ignore_index=False,若为True,表根据列名对齐合并,生成新的index。

<<< a=df.iloc[0:2,:]
<<< b=df.iloc[4:6,:]
<<< a.append(b,ignore_index=True)  
    A   B	C   D   E   F
0	0	1	2	3	4	5
1	6	7	8	9	10	11
2	24	25	26	27	28	29
3	30	31	32	33	34	35

<<< a=df.iloc[0:2,:]
<<< b=df.iloc[4:6,:]
<<< a.append(b)
    A	B	C	D	E   F
S1	0	1	2	3	4	5
S2	6	7	8	9	10	11
S5	24	25	26	27	28	29
S6	30	31	32	33	34	35

​ 在dataframe中,使用append方法进行表合并时,二者匹配不上的地方用NAN填充。

<<< df1=df.copy()
<<< df2=pd.DataFrame(np.arange(8).reshape(2,4),columns=<<<['s1','s2','s3','s4'])
<<< df_new=df1.append(df2,ignore_index=True)
<<< df_new
    A	B	C	D	E	F	S1	S2	s3	s4
0	0	1	2	3	4	5	NaN	NaN	NaN	NaN
1	6	7	8	9	10	11	NaN	NaN	NaN	NaN
2	12	13	14	15	16	17	NaN	NaN	NaN	NaN
3	18	19	20	21	22	23	NaN	NaN	NaN	NaN
4	24	25	26	27	28	29	NaN	NaN	NaN	NaN
5	30	31	32	33	34	35	NaN	NaN	NaN	NaN
6	36	37	38	39	40	41	NaN	NaN	NaN	NaN
7	NaN	NaN	NaN	NaN	NaN	NaN	0	1	2	3
8	NaN	NaN	NaN	NaN	NaN	NaN	4	5	6	7

​ 第三个参数为verify_integrity:默认为False 参数用于检查结果对象新连接轴上的索引是否有重复项,有的话引发 ValueError,可以看到这个参数的作用与ignore_index 是互斥的。 (如果 ignore_index = True ,则意味着index不能是重复的,而ignore_index = False ,则意味着index可以是重复的)

<<< df1=df.copy()
<<< df2=pd.DataFrame(np.arange(8).reshape(2,4),columns=   <<< ['G','H','I','J'],index=['S1','S8'],dtype=int)
<<< pd.set_option('precision',0)
<<< df_new=df1.append(df2,verify_integrity=False)
<<< df_new
    A	B	C	D	E	F	G	H	I	J
S1	0	1	2	3	4	5	NaN	NaN	NaN	NaN
S2	6	7	8	9	10	11	NaN	NaN	NaN	NaN
S3	12	13	14	15	16	17	NaN	NaN	NaN	NaN
S4	18	19	20	21	22	23	NaN	NaN	NaN	NaN
S5	24	25	26	27	28	29	NaN	NaN	NaN	NaN
S6	30	31	32	33	34	35	NaN	NaN	NaN	NaN
S7	36	37	38	39	40	41	NaN	NaN	NaN	NaN
S1	NaN	NaN	NaN	NaN	NaN	NaN	0	1	2	3
S8	NaN	NaN	NaN	NaN	NaN	NaN	4	5	6	7

注意:当需要连接的两个表的index有重复值时,设置ignore_index = True则会报错。

​ 第四个参数为sort:默认是False,该属性在pandas的0.23.0版本才有,若为True,则对两个表没匹配上的列名,进行排序,若为False,不排序。

<<< df1=pd.DataFrame(np.arange(8).reshape(2,4),columns=   <<< ['A1','B1','C1','D1'],index=['S1','S2'])
<<< df2=pd.DataFrame(np.arange(8).reshape(2,4),columns=   <<< ['A2','B2','C2','D2'],index=['S1','S3'])
<<< pd.set_option('precision',0)
<<< df_new=df1.append(df2,sort=True)
<<< df_new
    A1	A2	B1	B2	C1	C2	D1	D2
S1	0	NaN	1	NaN	2	NaN	3	NaN
S2	4	NaN	5	NaN	6	NaN	7	NaN
S1	NaN	0	NaN	1	NaN	2	NaN	3
S3	NaN	4	NaN	5	NaN	6	NaN	7

二、pd.concat([df_01,df_02])

描述:concat方法用以将两个或多个pandas对象根据轴(横向/纵向)进行拼接,concat函数是在pandas命名空间下的方法,因此需要通过pd.concat()的方式来引用。

语法:pd.concat(‘objs’, ‘axis=0’, “join=‘outer’”, ‘join_axes=None’, ‘ignore_index=False’, ‘keys=None’, ‘levels=None’, ‘names=None’, ‘verify_integrity=False’, ‘sort=None’, ‘copy=True’)

常用参数:

  • objs:要进行拼接的pandas对象,可用中括号[]将两个或多个对象括起来
  • axis:指定对象按照那个轴进行拼接,默认为0(纵向拼接),1为横向横向拼接
  • join:拼接的方式,inner为交集,outer为并集
  • join_axes:index的列表,仅在横向合并时使用,指明要将数据合并入哪个原表的index。
  • ignore_index:默认为False,如果设置为true,则无视表原来的轴标签,直接合并,合并后生成新的轴标签。
  • keys:表标识的列表,用来区分合并的表来自哪里。

下面,将对concat方法以上各个参数进行详细说明:

​ 第一个要学习的参数为objs:要进行拼接的pandas对象,可用中括号[]将两个或多个对象括起来。

1)对series进行拼接

<<< ser1=pd.Series(np.arange(9))
<<< ser2=pd.Series(np.arange(9))
# 对两个series对象进行拼接
<<< pd.concat([ser1,ser2])
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
dtype: int32
  1. 对DataFrame进行拼接
<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['e','f','g'])
# 对两个DataFrame对象进行拼接
<<< pd.concat([df1,df2])
    A   B	C	D	E   F
a	0	1	2	NaN	NaN	NaN
b	3	4	5	NaN	NaN	NaN
c	6	7	8	NaN	NaN	NaN
e	NaN	NaN	NaN	0	1	2
f	NaN	NaN	NaN	3	4	5
g	NaN	NaN	NaN	6	7	8

​ 第二个要学习的参数为axis:指定对象按照那个轴进行拼接,默认为0(纵向拼接),1为横向横向拼接。

<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['a','b','d'])
# 将数据对象df1和df2沿1轴进行拼接,即进行横向拼接
<<< pd.concat([df1,df2],axis=1)
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8

​ 注意:当对Series进行拼接时,设置axis=0进行纵向拼接的结果对象为Series,设置axis=1进行横向拼接的结果对象为DataFrame。

<<< ser1=pd.Series(np.arange(9))
<<< ser2=pd.Series(np.arange(9))
# 对Series进行拼接纵向拼接,结果认为Series对象
<<< a=pd.concat([ser1,ser2],axis=0)
<<< type(a)
pandas.core.series.Series
# 对Series进行拼接横向拼接,结果转换为DataFrame对象
<<< b=pd.concat([ser1,ser2],axis=1)
<<< type(b)
pandas.core.frame.DataFrame

​ 第三个要学习的参数为join:拼接的方式,inner为交集,outer为并集,横向拼接时由index的交/并集决定,纵向拼接时由columns的交/并集决定,同时,如果join=outer,匹配不上的地方以nan填充。

<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['a','b','d'])
# 将df1和df2进行横向合并,取二者的并集
<<< pd.concat([df1,df2],axis=1)
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8
# 将df1和df2进行横向合并,只取二者的交集
<<< pd.concat([df1,df2],axis=1,join='inner')
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5

​ 第四个要学习的参数为join_axes:以哪个数据对象的index/columns作为轴进行拼接,当进行横向拼接时,join_axes为index的列表,如需根据df1对齐数据,则会保留df1的index,再将df2的数据进行拼接;同理,纵向拼接时为columns的列表。

<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['a','b','d'])
# 根据df1的index对齐数据
<<< pd.concat([df1,df2],axis=1,join_axes=[df1.index])
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
# 根据df2的index对齐数据
<<< pd.concat([df1,df2],axis=1,join_axes=[df2.index])
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5
d	NaN	NaN	NaN	6	7	8

​ 第五个要学习的参数为ignore_index:默认为False,如果设置为true,则无视表原来的轴标签,直接合并,合并后生成新的轴标签。

​ 这里需要注意的是,与append方法只能进行纵向拼接不同,concat方法既可以进行横向拼接,也可以进行纵向拼接,若设置ignore_index=True,当进行横向拼接时,则无视原表的columns,直接合并,合并后生成默认的columns;同理,当进行纵向拼接时,则是忽略原表的index,生成新的index。

<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['a','b','d'])
# 横向拼接时,忽略的是columns,index仍起作用
<<< pd.concat([df1,df2],axis=1,ignore_index=True)
    0	1	2	3	4	5
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8
# 纵向拼接时,忽略的是index,columns仍起作用
pd.concat([df1,df2],axis=0,ignore_index=True)
    0	1	2	3	4	5
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8

​ 第六个要学习的参数为keys:表标识的列表,用来区分合并后的数据来源于哪个表,当ignore_index=True时,此参数的作用失效。

<<< df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['A','B','C'],index=['a','b','c'])
<<< df2=pd.DataFrame(np.arange(9).reshape(3,3),columns=   <<< ['D','E','F'],index=['a','b','d'])
# 设置ignore_index=True时,参数keys不起作用
<<< pd.concat([df1,df2],axis=1,ignore_index=True,keys=    <<< ['df1','df2'])
    0	1	2	3	4	5
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8
# 设置ignore_index=False,会根据keys的列表标识结果中的数据来源
<<< pd.concat([df1,df2],axis=1,ignore_index=False,keys=   <<< ['df1','df2'])
    df1	        df2
    A	B	C	D	E	F
a	0	1	2	0	1	2
b	3	4	5	3	4	5
c	6	7	8	NaN	NaN	NaN
d	NaN	NaN	NaN	6	7	8

总结:

  • append方法只能进行横向拼接,且只支持对两个对象进行拼接操作,但append支持单个对象的连接,此方法常用于循环中;
  • concat方法可用于横向或纵向的拼接,同时可以设置以并集或交集的方式拼接

如对append和concat方法还感兴趣,建议可前往查看官方文档:

1)https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html?highlight=append#pandas.DataFrame.append

2)pandas.concat - pandas 0.21.0 documentation