昊天SEO

beautifulsoup contents详解 获取文本

#coding:utf-8
html_doc = """


Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.



sdfsdf

"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, "lxml")
head = soup.select('p')

'''如何取出Once upon a time there were three little sisters; and their names were 这段文字呢?'''

print head
# 获取列表
print "==============="

print head[0].contents
# 根据tag进行分割

print "==============="

print head[0].contents[0]
#获取文本,大功告成



利用VPS下载YouTube视频并上传至百度云网盘

前一段时间接了个小任务,要把YouTube上的几百个视频下载回来,用百度云网盘分享给公司的同事供学习使用。

当老板发邮件问我这事儿是否能办时,我很快就回复说没问题,因为“从YouTube下载视频”这种事之前我也不是没做过。网上有一大批在线工具能够嗅探出YouTube视频的下载链接,然后直接用常规手段(迅雷啥的)下载就可以了。本地下载工具也不少,如著名的IDM就能够直接下载网页上嵌套的视频文件,几乎是万能的。不过,这次的任务有点复杂:一来要下载的文件太多了,我总不能重复操作几百次来下载每个视频吧;二来,还得上传到百度云,这么多视频,靠我家里那小水管,没个几天几夜恐怕是上传不完的,多费劲呀。

然后我就开始琢磨这件事能不能更自动化、更高效一些来实现。呃,放狗上Google搜了一下,找到了几个大杀器,参考了一下各位大神的教程后,我也拼凑出了一个解决方案。

长话短说:

在美国VPS上安装YouTube专用下载工具,远程下载想要的YouTube视频到服务器上;在VPS上安装百度云网盘上传工具,将已经下载到VPS中的视频上传到云端。

这个方案的好处很明显:用位于美国的VPS下载本身服务器就在美国的YouTbe网站视频,速度相当快(实测可以超过60MB/s)。从美国VPS上传文件到百度云,速度虽然受限(一般是几百Kb每秒,偶见超过1MB/s的),但仍比我在国内本地的上传网速要快得多。更重要的是,这个方案是高度自动化、批量进行的,不用手动对付每个视频。

短话长说:

接下来要写的详细教程,是一个大杂烩。你在网上能找到的特定教程,都是有特定限制条件的,例如你的VPS用的是啥操作系统,你要实现多大程度的自动化,等等。在完成这个教程的过程中,我也踩了不少坑,幸亏有万能的Google相助。

1 安装VPS操作系统

我用的是DigitalOcean(若使用我的推荐链接注册vultr,您的账户可以立即获得10美金,相当于免费获得两个月的使用权)最基础的套餐,每月5$,20G的SSD储存空间,1T流量。安装的操作系统是CentOS 7(请注意操作系统的选择,这很重要,后面我会提到原因),节点为旧金山(据说从大陆访问会比较快)。所以,如果你也是DO用户的话,可以完全照我这篇教程来做,准没问题。不是的话,请酌情参考就好了,其他问题我不负责解答哈,毕竟,我是一个伪技术流……

首先要赞下DigitalOcean的VPS用起来真是方便,点几下鼠标就能创建好一台服务器。理论上用SSH密钥登录会更安全,不过我偷懒,只是简单用LastPass生成一个几十位长度的随机密码,需要用到就复制粘贴一下好了(请大家不要向我学习……)。禁用root账号登录,修改SSH端口号什么的,这些安全措施也可以做下,我就不多说了,不太清楚的话请自行Google。

前面提过VPS的操作系统问题,为啥我用CentOS 7呢?因为这个版本默认自带的Python是2.7.5(截至本文发布时是这样的),而这个教程中会用到的百度云网盘Linux客户端(bypy)依赖的Python版本号为2.7x。一开始我装了个CentOS 6,其自带Python 6.x,不符合要求;Google了好多教程,还是没能装上Python 2.7x(在DO的这个系统上需要自行编译Python,各种报错很无奈);最后决定放弃CentOS 6,换成7,因为有那个工夫折腾Python,我还不如去看几集美剧是吧,呃,好像我又无意中暴露了什么……

2 安装youtube-dl

传说中的第一个大杀器终于粗线了:youtube-dl。这个工具可以用来下载一系列国外网站的在线视频,如油管家的。它既有本地客户端,也可以被部署在服务器上。

用Putty登入VPS后,依次执行以下两条命令:

wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl
chmod a+rx /usr/local/bin/youtube-dl

然后就没了。是的就是这么简单,现在你已经装完youtube-dl,可以在VPS上远程下载指定的YouTube视频了。

最简单的命令如下:

youtube-dl video-url

把上面的这个video-url换成具体某个YouTube视频的URL就可以直接下载了。注意,youtube-dl会默认下载视频到当前执行命令的目录下。

YouTube有Playlist功能,即播放列表,你可以用Google账号登录,然后就可以把若干视频添加保存到Playlist。如果你想要批量下载某个Playlist的视频,就可以把前面那条下载命令中的 video-url换成具体Playlist的链接。话句话说,想要一次下载多个YouTube视频,你可以考虑用这个Playlist功能。

不过呢,像本文开头提到的这个任务,如果我要手动添加几百个视频到Playlist那得累到手抽筋:我得先点开每一个视频的具体链接,然后再点两下鼠标才能将之添加进Playlist。好了不绕弯了,说这么多,就是想烘托出youtube-dl的另一个激动人心的功能:支持读取下载链接列表文件。这意味着我可以先把所有下载链接整理好后放进一个txt文件,然后让youtube-dl去读取这个txt文件就行了,接下来它就会自动下载全部的视频。该方法的命令如下:

youtube-dl -a video_url.txt

这里的 -a 参数表示读取外部链接文件。

具体细节操作在后面会提到,请耐心往下看……

3 安装byby

Byby是一个百度云网盘的Python客户端,可在Linux环境下通过命令行来操作。

在本文的操作环境下,安装byby需要更多的步骤,byby官网没有介绍那么详细,所以我参考了其他网友的教程。

# 安装Git

yum install git

# 安装Python Pip

yum -y install epel-release
yum -y install python-pip
yum clean all

说明:先装扩展源EPEL再装Python Pip才不会报错。

# 安装Requests

pip install requests

完成以上步骤后,才开始正式安装bypy。

依次执行以下三行命令:

git clone https://github.com/houtianze/bypy byby/
cd byby/
python bypy.py list

此时的命令行界面会出现提示,按照以下步骤来授权你的百度云账号使用bypy客户端:

  1. 左键选中用于百度账号授权的那行URL,即可复制该文本(在Putty中选中即可复制,不用Ctrl+C),在浏览器中打开此链接。
  2. 登录你的百度账号,然后复制授权码。
  3. 回到Putty命令行界面,粘贴授权码进去,回车即可开始启动授权操作。

稍等几秒,授权即可成功。

为了方便在任意目录下使用bypy命令,还需要设置如下:

cd /byby
cp bypy.py /usr/bin

完成以上设置后,如果要将VPS某个文件夹下的所有文件上传到百度云,最简单的命令如下:

bypy.py upload

还可以加参数,如 -v 可以显示上传进度:

bypy.py upload -v

默认情况下,本方案上传到百度云的文件全部存放在“我的应用数据 –> bypy”路径下。

4 下载与上传视频

在Putty命令行界面中创建一个专门的目录用于存放下载的视频文件,然后CD至此目录下,创建一个包含所有下载链接的txt文件(使用vi命令即可)。在此目录下,执行下载命令:

youtube-dl -c -i -a video_url.txt

呃,眼尖的你可能已经发现了,上面这条命令好像多出了两个参数?没错,youtube-dl提供了很多有用的附加参数,这里用到的是:

  • -c 断点续传(如果由于意外原因下载中断了,再次启动下载命令时,可以实现断点续传)
  • -i 忽略报错(如果出现某些链接无法打开或者下载的问题,会自动绕过,继续下载其他视频)

下载的话一般很快,但是上传到百度云并没有那么快。为了防止关闭Putty窗口后,下载/上传进程就断掉了,可以考虑使用Screen远程会话管理功能。简单介绍就是,有了这玩意儿,你可以在Putty中启动下载或者上传进程,让其在后台运行,然后关掉Putty离开电脑旁该干啥干啥去,有需要的话回来重新打开窗口就可以看到进程依然在跑。

要检查操作系统是否支持screen,可以直接输入screen然后回车,如果提示找不到这个命令,那就是系统还没安装好这个东西。在CentOS 7中安装screen很简单,一条命令而已:

yum install screen

安装完毕后,输入 screen -S sessionname 即可创建一个名为 sessionname 的screen会话窗口。在此窗口中执行想要做的任务,然后同时按下 ctrl + a,松开后按下 d,即可将此session放到后台去运行,并回到之前的对话窗口。在非screen对话窗口中,输入 screen -ls 可以列举已有的screen进程。输入 “screen -r 进程代码” 就可以再次调出正在运行的screen进程。

由于我要上传到百度云的文件太多了,我不可能守在电脑前等它完成所有任务,所以screen这个功能还是非常实用的。

本文主要参考链接:
https://github.com/rg3/youtube-dl/
https://github.com/houtianze/bypy
http://yesido.info/2015/03/linux-upload-document-to-baidu-cloud/

破解有道翻译反爬虫机制

web端的有道翻译,在之前是直接可以爬的。也就是说只要获取到了他的接口,你就可以肆无忌惮的使用他的接口进行翻译而不需要支付任何费用。那么自从有道翻译推出他的API服务的时候,就对这个接口做一个反爬虫机制(如果大家都能免费使用到他的翻译接口,那他的 API 服务怎么赚钱)。这个反爬虫机制在爬虫领域算是一个非常经典的技术手段。那么他的反爬虫机制原理是什么?如何破解?接下来带大家一探究竟。

一、正常的爬虫流程:

如果你要爬取他的翻译接口,这个流程还是不能少的。首先我们打开有道翻译的链接:http://fanyi.youdao.com/。然后在页面中右键->检查->Network 项。这时候就来到了网络监听窗口,以后你在这个页面中发送的所有网络请求,都会在Network这个地方显示出来。接着我们在翻译的窗口输入我们需要翻译的文字,比如输入hello。然后点击自动翻译按钮,那么接下来在下面就可以看到浏览器给有道发送的请求,这里截个图看看: 01.png

在上图,我们可以看到发送了很多的网络请求,这里我们点击第一个网络请求进行查看: 02.png

可以看到,我们在点击自动翻译的时候,发送的请求就是上图中Request URL的那个URL,然后我们再点击那个Response,我们可以看到返回的结果: 03.png

并且,现在我们再回到Headers的地方,然后滚动到最下面,可以看到有一个Form Data的地方,这个下面展示了许多的数据,这些数据就是你在点击__翻译__的时候浏览器给服务器发送的数据: 04.png

对其中几个比较重要的数据进行解释:

  • i:需要进行翻译的字符串,这个地方我们输入的是 hello。
  • salt:加密用到的盐。这个是我们破解有道反爬虫机制的关键点,后面会讲到。
  • sign:签名字符串。也是破解反爬虫机制的关键点。

其他的数据类型暂时就不怎么重要了,都是固定写法,我们后面写代码的时候直接鞋子就可以了。到现在为止,我们就可以写一个简单的爬虫,去调用有道翻译的接口了。这里我们使用的网络请求库是Python3自带的urllib,相关代码如下:

# 导入需要的库
import urllib.request
import urllib.parse
import json

# 等待用户输入需要翻译的单词
content = input('请输入需要翻译的单词:')

# 有道翻译的 url 链接
url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule&sessionFrom=null'

# 发送给有道服务器的数据
data = {}

# 需要翻译的文字
data['i'] = content
# 下面这些都先按照我们之前抓包获取到的数据
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '1500349255670'
data['sign'] = '997742c66698b25b43a3a5030e1c2ff2'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CL1CKBUTTON'
data['typoResult'] = 'true'

# 对数据进行编码处理
data = urllib.parse.urlencode(data).encode('utf-8')

# 创建一个 Request 对象,把 url 和 data 传进去,并且需要注意的使用的是 POST 请求
request = urllib.request.Request(url=self.url, data=data, method='POST')
# 打开这个请求
response = urllib.request.urlopen(request)
# 读取返回来的数据
result_str = response.read().decode('utf-8')
# 把返回来的 json 字符串解析成字典
result_dict = json.loads(result_str)

# 获取翻译结果
print('翻译的结果是:%s' % result_dict)

我们运行这个文件后,当我们输入的是hello的时候,我们可以得到哈罗的这个正确的翻译结果。而当我们输入其他需要翻译的字符串的时候,比如输入i love you,那么就会得到一个错误代码{"errorCode":50}。这就奇怪了,有道词典不可能只能翻译一个英文单词吧。而这个,就是有道词典的反爬虫机制。接下来我们就来破解有道词典的反爬虫机制。

二、破解反爬虫机制:

我们可以多次的进行翻译,并且每次翻译后都去查看翻译的时候发送的这个网络请求,比较每次翻译时候发送的Form Data的值。我们注意到,Form Data在每次发送网络请求的时候,只有isalt以及sign这三个是不同的,其他的数据都是一样的,这里我用helloworld两个单词翻译时候Form Data的数据进行比较: 05.png

06.png

图中的Form Data也证实了我刚刚所说的,就是除了isalt以及sign是不一样的。其余都是一样的。而i不一样是很正常的。因为i代表的是要翻译的字符串,这个不同是很正常。而saltsign这两个东西不一样,是怎么产生的呢?这里我们可以分析一下,这两个值在每次请求的时候都不一样,只有两种情况:第一是每次翻译的时候,浏览器会从有道服务器获取一下这两个值。这样可以达到每次翻译的时候值不同的需求。第二是在本地,用JS代码按照一定的规则生成的。那么我们首先来看第一个情况,我们可以看到在每次发送翻译请求的时候,并没有一个请求是专门用来获取这两个值的: 07.png

所以就可以排除第一种情况。就只剩下一种可能,那就是在本地自己生成的,如果是在本地自己生成的,那么规则是什么呢?这里我们点击网页,查看网页源代码,查找所有的JS文件,我们找到那个fanyi.js: 08.png

然后点击这个文件,跳转到这个源文件中,然后全选所有的代码,复制下来,再打开站长工具:http://tool.chinaz.com/Tools/jsformat.aspx。把代码复制进去后,点击格式化: 09.png

然后把格式化后的代码,复制下来,用sublime或者pycharm打开都可以,然后搜索salt,可以找到相关的代码: 10.png

这里我们就可以发现所有的值的生成原理了。这里来做个简介:

  • d:代表的是需要翻译的字符串。
  • f:当前时间的时间戳加上 0-10 的随机字符串。
  • u:一个常量——fanyideskweb
  • c:一个常量——rY0D^0'nM0}g5Mm1z%1G4
  • salt:就是f变量,时间戳。
  • sign:使用的是u + d + f + cmd5的值。

知道saltsign的生成原理后,我们就可以写Python代码,来对接他的接口了,以下是相关代码:

import urllib.request

import urllib.parse
import json
import time
import random
import hashlib

content = input('请输入需要翻译的句子:')

url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule&sessionFrom=https://www.google.com/'

data = {}

u = 'fanyideskweb'
d = content
f = str(int(time.time()*1000) + random.randint(1,10))
c = 'rY0D^0\'nM0}g5Mm1z%1G4'

sign = hashlib.md5((u + d + f + c).encode('utf-8')).hexdigest()

data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = f
data['sign'] = sign
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CL1CKBUTTON'
data['typoResult'] = 'true'

data = urllib.parse.urlencode(data).encode('utf-8')
request = urllib.request.Request(url=url,data=data,method='POST')
response = urllib.request.urlopen(request)

print(response.read().decode('utf-8'))

写在最后:

像以上这种,通过用JS在本地生成随机字符串的反爬虫机制,在爬虫的时候是经常会遇到的一个问题。希望通过以上的讲解,能为大家提供一种思路。以后再碰到这种问题的时候知道该如何解决。这样本篇文章的目的也就达到了。

老司机用 Python 多线程爬取表情包

多线程爬取表情包

有一个网站,叫做“斗图啦”,网址是:https://www.doutula.com/。这里面包含了许许多多的有意思的斗图图片,还蛮好玩的。有时候为了斗图要跑到这个上面来找表情,实在有点费劲。于是就产生了一个邪恶的想法,可以写个爬虫,把所有的表情都给爬下来。这个网站对于爬虫来讲算是比较友好了,他不会限制你的headers,不会限制你的访问频率(当然,作为一个有素质的爬虫工程师,爬完赶紧撤,不要把人家服务器搞垮了),不会限制你的 IP 地址,因此技术难度不算太高。但是有一个问题,因为这里要爬的是图片,而不是文本信息,所以采用传统的爬虫是可以完成我们的需求,但是因为是下载图片所以速度比较慢,可能要爬一两个小时都说不准。因此这里我们准备采用多线程爬虫,一下可以把爬虫的效率提高好几倍。

一、分析网站和爬虫准备工作:

构建所有页面 URL 列表:

这里我们要爬的页面不是“斗图啦”首页,而是最新表情页面https://www.doutula.com/photo/list/,这个页面包含了所有的表情图片,只是是按照时间来排序的而已。我们把页面滚动到最下面,可以看到这个最新表情使用的是分页,当我们点击第二页的时候,页面的URL变成了https://www.doutula.com/photo/list/?page=2,而我们再回到第一页的时候,page又变成了1,所以这个翻页的URL其实很简单,前面这一串https://www.doutula.com/photo/list/?page=都是固定的,只是后面跟的数字不一样而已。并且我们可以看到,这个最新表情总共是有 869 页,因此这里我们可以写个非常简单的代码,来构建一个从 1 到 869 的页面的URL列表:

# 全局变量,用来保存页面的 URL 的
PAGE_URL_LIST = []
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)
获取一个页面中所有的表情图片链接:

我们已经拿到了所有页面的链接,但是还没有拿到每个页面中表情的链接。经过分析,我们可以知道,其实每个页面中表情的HTML元素构成都是一样的,因此我们只需要针对一个页面进行分析,其他页面按照同样的规则,就可以拿到所有页面的表情链接了。这里我们以第一页为例,跟大家讲解。首先在页面中右键->检查->Elements,然后点击Elements最左边的那个小光标,再把鼠标放在随意一个表情上,这样下面的代码就定位到这个表情所在的代码位置了: 01.png可以看到,这个img标签的class是等于img-responsive lazy image_dtz,然后我们再定位其他表情的img标签,发现所有的表情的img标签,他的class都是img-responsive lazy image_dtz: 02.png03.png

因此我们只要把数据从网上拉下来,然后再根据这个规则进行提取就可以了。这里我们使用了两个第三方库,一个是requests,这个库是专门用来做网络请求的。第二个库是bs4,这个库是专门用来把请求下来的数据进行分析和过滤用的,如果没有安装好这两个库的,可以使用以下代码进行安装(我使用的是 python2.7 的版本):

# 安装 requests
pip install requests
# 安装 bs4
pip install bs4
# 安装 lxml 解析引擎
pip install lxml

然后我们以第一个页面为例,跟大家讲解如何从页面中获取所有表情的链接:

# 导入 requests 库
import requests
# 从 bs4 中导入 BeautifulSoup
from bs4 import BeautifulSoup

# 第一页的链接
url = 'https://www.doutula.com/photo/list/?page=1'
# 请求这个链接
response = requests.get(url)
# 使用返回的数据,构建一个 BeautifulSoup 对象
soup = BeautifulSoup(response.content,'lxml')
# 获取所有 class='img-responsive lazy image_dtz'的 img 标签
img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
for img in img_list:
	# 因为 src 属性刚开始获取的是 loading 的图片,因此使用 data-original 比较靠谱
	print img['data-original']

这样我们就可以在控制台看到本页中所有的表情图片的链接就全部都打印出来了。

下载图片:

有图片链接后,还要对图片进行下载处理,这里我们以一张图片为例:http://ws2.sinaimg.cn/bmiddle/9150e4e5ly1fhpi3ysfocj205i04aa9z.jpg,来看看Python中如何轻轻松松下载一张图片:

import urllib
url = 'http://ws2.sinaimg.cn/bmiddle/9150e4e5ly1fhpi3ysfocj205i04aa9z.jpg'
urllib.urlretrieve(url,filename='test.jpg')

这样就可以下载一张图片了。

结合以上三部分内容:

以上三部分,分别对,如何构建所有页面的URL,一个页面中如何获取所有表情的链接以及下载图片的方法。接下来把这三部分结合在一起,就可以构建一个完整但效率不高的爬虫了:

# 导入 requests 库
import requests
# 从 bs4 中导入 BeautifulSoup
from bs4 import BeautifulSoup
import urllib
import os

# 全局变量,用来保存页面的 URL 的
PAGE_URL_LIST = []
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)


for page_url in PAGE_URL_LIST:
    # 请求这个链接
    response = requests.get(page_url)
    # 使用返回的数据,构建一个 BeautifulSoup 对象
    soup = BeautifulSoup(response.content,'lxml')
    # 获取所有 class='img-responsive lazy image_dtz'的 img 标签
    img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
    for img in img_list:
        # 因为 src 属性刚开始获取的是 loading 的图片,因此使用 data-original 比较靠谱
        src = img['data-original']
        # 有些图片是没有 http 的,那么要加一个 http
        if not src.startswith('http'):
			src = 'http:'+ src
        # 获取图片的名称
        filename = src.split('/').pop()
        # 拼接完整的路径
        path = os.path.join('images',filename)
        urllib.urlretrieve(src,path)

以上这份代码。可以完整的运行了。但是效率不高,毕竟是在下载图片,要一个个排队下载。如果能够采用多线程,在一张图片下载的时候,就完全可以去请求其他图片,而不用继续等待了。因此效率比较高,以下将该例子改为多线程来实现。

二、多线程下载图片:

这里多线程我们使用的是Python自带的threading模块。并且我们使用了一种叫做生产者和消费者的模式,生产者专门用来从每个页面中获取表情的下载链接存储到一个全局列表中。而消费者专门从这个全局列表中提取表情链接进行下载。并且需要注意的是,在多线程中使用全局变量要用来保证数据的一致性。以下是多线程的爬虫代码(如果有看不懂的,可以看视频,讲解很仔细):

#encoding: utf-8

import urllib
import threading
from bs4 import BeautifulSoup
import requests
import os
import time

# 表情链接列表
FACE_URL_LIST = []
# 页面链接列表
PAGE_URL_LIST = []
# 构建 869 个页面的链接
BASE_PAGE_URL = 'https://www.doutula.com/photo/list/?page='
for x in range(1, 870):
    url = BASE_PAGE_URL + str(x)
    PAGE_URL_LIST.append(url)

# 初始化锁
gLock = threading.Lock()

# 生产者,负责从每个页面中提取表情的 url
class Producer(threading.Thread):
    def run(self):
        while len(PAGE_URL_LIST) > 0:
            # 在访问 PAGE_URL_LIST 的时候,要使用锁机制
            gLock.acquire()
            page_url = PAGE_URL_LIST.pop()
            # 使用完后要及时把锁给释放,方便其他线程使用
            gLock.release()
            response = requests.get(page_url)
            soup = BeautifulSoup(response.content, 'lxml')
            img_list = soup.find_all('img', attrs={'class': 'img-responsive lazy image_dta'})
            gLock.acquire()
            for img in img_list:
                src = img['data-original']
                if not src.startswith('http'):
                    src = 'http:'+ src
                # 把提取到的表情 url,添加到 FACE_URL_LIST 中
                FACE_URL_LIST.append(src)
            gLock.release()
            time.sleep(0.5)

# 消费者,负责从 FACE_URL_LIST 提取表情链接,然后下载
class Consumer(threading.Thread):
    def run(self):
        print '%s is running' % threading.current_thread
        while True:
            # 上锁
            gLock.acquire()
            if len(FACE_URL_LIST) == 0:
                # 不管什么情况,都要释放锁
                gLock.release()
                continue
            else:
                # 从 FACE_URL_LIST 中提取数据
                face_url = FACE_URL_LIST.pop()
                gLock.release()
                filename = face_url.split('/')[-1]
                path = os.path.join('images', filename)
                urllib.urlretrieve(face_url, filename=path)

if __name__ == '__main__':
    # 2 个生产者线程,去从页面中爬取表情链接
    for x in range(2):
        Producer().start()

    # 5 个消费者线程,去从 FACE_URL_LIST 中提取下载链接,然后下载
    for x in range(5):
        Consumer().start()

写在最后:

本教程采用多线程来完成表情的爬取,可以让爬取效率高出很多倍。Python的多线程虽然有GIL全局解释器锁,但在网络IO处理这一块表现还是很好的,不用在一个地方一直等待。以上这个例子就很好的说明了多线程的好处。

pandas数据筛选及过滤、字符串提取与操作、画图

1、pandas 库之数据筛选及过滤:
主要介绍 pandas 库的用途,以及安装方法。并介绍它的用途,包括数据的选择和筛选。
<br>
2、pandas 库之字符串提取与操作:
本课时主要介绍使用 pandas 库进行字符串数据的提取与其他操作,使得 pandas 库可以操作字符串数据。
<br>
3、pandas 库之散点图:
本课时主要介绍使用 pandas 绘制散点图以及轨迹的方法,通过pandas库也可以绘制图形。
<br>
4、pandas 库之直方图:
本课时主要介绍使用 pandas 绘制直方图,柱形图和箱形图的方法。

In [278]:
'''

第一节:
#pandas 库之数据筛选及过滤 ************************************

'''
Out[278]:
'\n\n\xe7\xac\xac\xe4\xb8\x80\xe8\x8a\x82\xef\xbc\x9a\n#pandas \xe5\xba\x93\xe4\xb9\x8b\xe6\x95\xb0\xe6\x8d\xae\xe7\xad\x9b\xe9\x80\x89\xe5\x8f\x8a\xe8\xbf\x87\xe6\xbb\xa4 ************************************\n\n'
In [3]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
dates = pd.date_range('20160715',periods=6)
dates
Out[3]:
DatetimeIndex(['2016-07-15', '2016-07-16', '2016-07-17', '2016-07-18',
               '2016-07-19', '2016-07-20'],
              dtype='datetime64[ns]', freq='D', tz=None)
In [115]:
df = pd.DataFrame(np.random.rand(6,4),index=dates,columns=list('ABCD'))
df
Out[115]:
A B C D
2016-07-15 0.257509 0.264885 0.540292 0.485975
2016-07-16 0.629827 0.079777 0.338386 0.187553
2016-07-17 0.375727 0.700579 0.384695 0.909140
2016-07-18 0.418069 0.308024 0.451242 0.758287
2016-07-19 0.114625 0.397367 0.888026 0.358038
2016-07-20 0.454836 0.182236 0.158715 0.002074
In [12]:
df2 = pd.DataFrame({'A':np.random.randn(6)})#定义一个随机数字的DF数据表
df2
Out[12]:
A
0 -2.058364
1 0.817129
2 1.630002
3 0.673549
4 0.416836
5 0.033933
In [22]:
df['A']#调用A列
Out[22]:
2016-07-15    0.736079
2016-07-16    0.183782
2016-07-17    0.198436
2016-07-18    0.865754
2016-07-19    0.095199
2016-07-20    0.731607
Freq: D, Name: A, dtype: float64
In [25]:
df[1:3]#调用从1~2的行数
Out[25]:
A B C D
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-17 0.198436 0.452805 0.275851 0.119994
In [27]:
df['20160715':'20160718']#调用时间序列XX~XX时间的数据
Out[27]:
A B C D
2016-07-15 0.736079 0.959340 0.830599 0.627481
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-17 0.198436 0.452805 0.275851 0.119994
2016-07-18 0.865754 0.584943 0.381434 0.966995
In [30]:
df.loc['20160715':'20160718',['A','B']]#.loc是通过标签来切片
Out[30]:
A B
2016-07-15 0.736079 0.959340
2016-07-16 0.183782 0.740787
2016-07-17 0.198436 0.452805
2016-07-18 0.865754 0.584943
In [31]:
df.loc['20160715',['A']]#.loc是通过标签来切片
Out[31]:
A    0.736079
Name: 2016-07-15 00:00:00, dtype: float64
In [38]:
df.at[dates[0],'B']#.at是通过数据位置来取数
Out[38]:
0.95934014640962595
In [40]:
df.head(3)#查看前3行
Out[40]:
A B C D
2016-07-15 0.736079 0.959340 0.830599 0.627481
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-17 0.198436 0.452805 0.275851 0.119994
In [41]:
df.tail()#默认查看后5行
Out[41]:
A B C D
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-17 0.198436 0.452805 0.275851 0.119994
2016-07-18 0.865754 0.584943 0.381434 0.966995
2016-07-19 0.095199 0.431479 0.394274 0.041155
2016-07-20 0.731607 0.018931 0.694380 0.189079
In [42]:
df.index#数据表的索引
Out[42]:
DatetimeIndex(['2016-07-15', '2016-07-16', '2016-07-17', '2016-07-18',
               '2016-07-19', '2016-07-20'],
              dtype='datetime64[ns]', freq='D', tz=None)
In [43]:
df.columns#查看字段表名
Out[43]:
Index([u'A', u'B', u'C', u'D'], dtype='object')
In [45]:
df.values#查看数据
Out[45]:
array([[ 0.73607859,  0.95934015,  0.83059865,  0.62748078],
       [ 0.18378218,  0.74078748,  0.58965485,  0.16701782],
       [ 0.19843576,  0.4528047 ,  0.27585071,  0.1199943 ],
       [ 0.86575368,  0.58494297,  0.38143413,  0.96699532],
       [ 0.09519916,  0.43147948,  0.39427373,  0.0411551 ],
       [ 0.73160684,  0.01893109,  0.69437963,  0.18907934]])
In [46]:
df.describe()#查询表中的描述统计
Out[46]:
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.468476 0.531381 0.527699 0.351954
std 0.344089 0.318945 0.212599 0.364780
min 0.095199 0.018931 0.275851 0.041155
25% 0.187446 0.436811 0.384644 0.131750
50% 0.465021 0.518874 0.491964 0.178049
75% 0.734961 0.701826 0.668198 0.517880
max 0.865754 0.959340 0.830599 0.966995
In [63]:
df.T#反转
Out[63]:
2016-07-15 00:00:00 2016-07-16 00:00:00 2016-07-17 00:00:00 2016-07-18 00:00:00 2016-07-19 00:00:00 2016-07-20 00:00:00
A 0.736079 0.183782 0.198436 0.865754 0.095199 0.731607
B 0.959340 0.740787 0.452805 0.584943 0.431479 0.018931
C 0.830599 0.589655 0.275851 0.381434 0.394274 0.694380
D 0.627481 0.167018 0.119994 0.966995 0.041155 0.189079
In [ ]:
#df2 = pd.DataFrame([1,2,3],index=['a','b','c'])
#df2
In [60]:
df.sort()#排序 从小到大排序
Out[60]:
A B C D
2016-07-15 0.736079 0.959340 0.830599 0.627481
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-17 0.198436 0.452805 0.275851 0.119994
2016-07-18 0.865754 0.584943 0.381434 0.966995
2016-07-19 0.095199 0.431479 0.394274 0.041155
2016-07-20 0.731607 0.018931 0.694380 0.189079
In [64]:
df.sort('C')#根据C这个列表进行从小到大排序
Out[64]:
A B C D
2016-07-17 0.198436 0.452805 0.275851 0.119994
2016-07-18 0.865754 0.584943 0.381434 0.966995
2016-07-19 0.095199 0.431479 0.394274 0.041155
2016-07-16 0.183782 0.740787 0.589655 0.167018
2016-07-20 0.731607 0.018931 0.694380 0.189079
2016-07-15 0.736079 0.959340 0.830599 0.627481
In [77]:
df1 = pd.DataFrame(np.random.randn(6,4),columns=list('abcd'))
df1
Out[77]:
a b c d
0 1.225333 -0.694005 -0.868498 2.540235
1 -0.089846 0.075165 0.722056 0.261062
2 1.087733 -0.590180 0.139107 -0.768135
3 1.587061 -0.176495 -0.784779 0.186754
4 0.978927 0.244961 -0.576200 3.148124
5 -0.057562 0.575155 0.550383 -0.859195
In [78]:
df1[df1.d>0]
Out[78]:
a b c d
0 1.225333 -0.694005 -0.868498 2.540235
1 -0.089846 0.075165 0.722056 0.261062
3 1.587061 -0.176495 -0.784779 0.186754
4 0.978927 0.244961 -0.576200 3.148124
In [97]:
df1[df1.c<0][['a','b']]
Out[97]:
a b
0 1.225333 -0.694005
3 1.587061 -0.176495
4 0.978927 0.244961
In [280]:
'''

第二节:
#pandas 库之字符串提取与操作 ************************************

'''
Out[280]:
'\n\n\xe7\xac\xac\xe4\xba\x8c\xe8\x8a\x82\xef\xbc\x9a\n#pandas \xe5\xba\x93\xe4\xb9\x8b\xe5\xad\x97\xe7\xac\xa6\xe4\xb8\xb2\xe6\x8f\x90\xe5\x8f\x96\xe4\xb8\x8e\xe6\x93\x8d\xe4\xbd\x9c ************************************\n\n'
In [227]:
s = pd.Series(list('ABCDEF'))
s
Out[227]:
0    A
1    B
2    C
3    D
4    E
5    F
dtype: object
In [228]:
s.str.lower()#.lower()将字符串转化为小写
Out[228]:
0    a
1    b
2    c
3    d
4    e
5    f
dtype: object
In [229]:
s.str.upper()#.upper()将字符串转化为大写
Out[229]:
0    A
1    B
2    C
3    D
4    E
5    F
dtype: object
In [230]:
s.str.len()#.str.len()求字符串长度
Out[230]:
0    1
1    1
2    1
3    1
4    1
5    1
dtype: int64
In [231]:
s.str.split('')#.split('')切割字符串('这里写分隔符')
Out[231]:
0    [A]
1    [B]
2    [C]
3    [D]
4    [E]
5    [F]
dtype: object
In [232]:
s.str.replace('A','Z')#.replace()将字符串替换成另外的
Out[232]:
0    Z
1    B
2    C
3    D
4    E
5    F
dtype: object
In [233]:
s1 = pd.Series(['a1','a2','a3','a4'])#创建一个新的序列
s1
Out[233]:
0    a1
1    a2
2    a3
3    a4
dtype: object
In [238]:
s1.str.extract('[ab](\d)')#提取
Out[238]:
0    1
1    2
2    3
3    4
dtype: object
In [239]:
s1.str.extract('([abc])(\d)')#提取
Out[239]:
0 1
0 a 1
1 a 2
2 a 3
3 a 4
In [240]:
s1.str.extract('(?P<str>[abc])(?P<digit>\d)')#提取
Out[240]:
str digit
0 a 1
1 a 2
2 a 3
3 a 4
In [253]:
s2 = pd.Series(['a','B','c','d'])
a_z = r'[a-z]'#正则表达式
s2.str.contains(a_z)#.contains()检测列表里面有没有包含指定的内容
Out[253]:
0     True
1    False
2     True
3     True
dtype: bool
In [267]:
s3 = pd.Series(['ab','Ba','ac','d'])
s3
Out[267]:
0    ab
1    Ba
2    ac
3     d
dtype: object
In [268]:
s3.str.contains('^a')#.contains('^a')检测列表里面有没有包含指定的a开头的内容并返回bool值
Out[268]:
0     True
1    False
2     True
3    False
dtype: bool
In [271]:
s3.str.startswith('a')#.startswith('')检测列表里面有没有包含指定的a开头的内容并返回bool值,同上
Out[271]:
0     True
1    False
2     True
3    False
dtype: bool
In [272]:
s3.str.endswith('a')#.endswith('')检测列表里面有没有包含指定的a结尾的内容并返回bool值
Out[272]:
0    False
1     True
2    False
3    False
dtype: bool
In [275]:
s3.str.contains('a$')#.contains('a$')检测列表里面有没有包含指定的a结尾的内容并返回bool值,同上
Out[275]:
0    False
1     True
2    False
3    False
dtype: bool
In [281]:
'''

第三节:
#pandas 库之散点图 ************************************

'''
Out[281]:
'\n\n\xe7\xac\xac\xe4\xb8\x89\xe8\x8a\x82\xef\xbc\x9a\n#pandas \xe5\xba\x93\xe4\xb9\x8b\xe6\x95\xa3\xe7\x82\xb9\xe5\x9b\xbe ************************************\n\n'
In [14]:
duqu = pd.read_csv('603318.XSHG_1m.csv')#读取.csv文件:pd.read_csv('路径')
duqu.head()
Out[14]:
Unnamed: 0 open close high low volume money
0 2015-04-24 09:30:00 9.39 9.39 9.39 9.39 100 939
1 2015-04-24 09:31:00 9.39 9.39 9.39 9.39 2000 18780
2 2015-04-24 09:32:00 9.39 9.39 9.39 9.39 1000 9390
3 2015-04-24 09:33:00 9.39 9.39 9.39 9.39 1000 9390
4 2015-04-24 09:34:00 9.39 9.39 9.39 9.39 0 0
In [306]:
pl = duqu.plot(kind='scatter',x='volume',y='money').get_figure()
#.plot(kind='种类函数',x轴,y轴)绘制散点图
pl.savefig('1.jpg')#保存图片
In [20]:
'''

第四节:
#pandas 库之柱形图\直方图\箱型图 ************************************

'''
Out[20]:
'\n\n\xe7\xac\xac\xe5\x9b\x9b\xe8\x8a\x82\xef\xbc\x9a\n#pandas \xe5\xba\x93\xe4\xb9\x8b\xe6\x9f\xb1\xe5\xbd\xa2\xe5\x9b\xbe\\\xe7\x9b\xb4\xe6\x96\xb9\xe5\x9b\xbe\\\xe7\xae\xb1\xe5\x9e\x8b\xe5\x9b\xbe ************************************\n\n'
In [9]:
df6 = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
pd.set_option('mpl_style','default')#设置风格kind='bar' 为柱形图
plt6 = df6.plot(kind='bar',stacked=True).get_figure()#.plot(kind='风格',stacked=是否堆积效果bool)
plt6.savefig('2.jpg')
In [19]:
df7 = pd.DataFrame(np.random.rand(100,4),columns=list('abcd'))
d8 = df7['a'].hist().get_figure()#绘制直方图
d8.savefig('3.jpg')
In [33]:
pd.set_option('mpl_style','default')
fig,ax = plt.subplots()
df9 = pd.DataFrame(np.random.rand(100,2),columns=list('ab'))
df9.boxplot(ax=ax)#绘制箱型图
plt.show()

 

 

pandas 入门(一)

10 Minutes to pandas

请参阅官方文档

In [1]:
# 设置为 inline 风格
%matplotlib inline
In [2]:
# 包导入
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

创建数据集对象

In [3]:
# Series 对象可以理解为一维数组
s = pd.Series([1, 3, 5, np.nan, 6, 8])
s
Out[3]:
0     1
1     3
2     5
3   NaN
4     6
5     8
dtype: float64
In [4]:
# DataFrame 对象可以理解为二维数组,可以指定索引格式
dates = pd.date_range('20160301', periods=6)
# periods:integer或None,默认值是None,表示你要从这个函数产生多少个日期索引值;如果是None的话,那么start和end必须不能为None。
dates
Out[4]:
DatetimeIndex(['2016-03-01', '2016-03-02', '2016-03-03', '2016-03-04',
               '2016-03-05', '2016-03-06'],
              dtype='datetime64[ns]', freq='D')
In [5]:
df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
df
Out[5]:
A B C D
2016-03-01 1.188983 -1.150119 -0.700588 0.439065
2016-03-02 -2.041544 1.084507 -0.335441 1.969754
2016-03-03 1.204151 -1.277714 -0.230671 0.629063
2016-03-04 -0.352351 -1.701585 -0.034294 -0.330139
2016-03-05 0.627601 -0.292939 0.457975 2.262402
2016-03-06 -1.121869 -0.533223 0.627452 0.412665
In [6]:
df.values
Out[6]:
array([[ 1.18898298, -1.15011854, -0.70058776,  0.43906549],
       [-2.04154443,  1.08450747, -0.33544069,  1.96975377],
       [ 1.2041512 , -1.27771421, -0.23067059,  0.62906316],
       [-0.35235094, -1.70158492, -0.03429361, -0.33013878],
       [ 0.62760104, -0.29293918,  0.45797463,  2.26240237],
       [-1.12186945, -0.53322343,  0.6274522 ,  0.41266481]])
In [7]:
# 使用字典来创建:key 为 DataFrame 的列;value 为对应列下的值
df = pd.DataFrame({
                  'A': 1,
                  'B': pd.Timestamp('20160301'),
                  'C': range(4),
                  'D': np.arange(5, 9),
                  'E': 'text',
                  'F': ['AA', 'BB', 'CC', 'DD']})
df
Out[7]:
A B C D E F
0 1 2016-03-01 0 5 text AA
1 1 2016-03-01 1 6 text BB
2 1 2016-03-01 2 7 text CC
3 1 2016-03-01 3 8 text DD
In [8]:
df.dtypes
Out[8]:
A             int64
B    datetime64[ns]
C             int64
D             int64
E            object
F            object
dtype: object
In [9]:
df.A
Out[9]:
0    1
1    1
2    1
3    1
Name: A, dtype: int64
In [10]:
type(df.A)
Out[10]:
pandas.core.series.Series

查看数据

In [11]:
# 创建数据集
n_rows = 6
dates = pd.date_range('20160301', periods=n_rows)
df = pd.DataFrame(np.random.randn(n_rows, 4), index=dates, columns=list('ABCD'))
df
Out[11]:
A B C D
2016-03-01 1.313419 0.826457 -1.574146 0.525008
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923
In [12]:
df.shape
Out[12]:
(6, 4)
In [13]:
df.head()
Out[13]:
A B C D
2016-03-01 1.313419 0.826457 -1.574146 0.525008
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
In [14]:
df.head(3)
Out[14]:
A B C D
2016-03-01 1.313419 0.826457 -1.574146 0.525008
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
In [15]:
df.tail()
Out[15]:
A B C D
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923
In [16]:
df.tail(2)
Out[16]:
A B C D
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923
In [17]:
df.index
Out[17]:
DatetimeIndex(['2016-03-01', '2016-03-02', '2016-03-03', '2016-03-04',
               '2016-03-05', '2016-03-06'],
              dtype='datetime64[ns]', freq='D')
In [18]:
df.columns
Out[18]:
Index([u'A', u'B', u'C', u'D'], dtype='object')
In [19]:
df.values
Out[19]:
array([[ 1.31341924,  0.82645709, -1.57414606,  0.52500758],
       [ 0.02839742, -1.00934929,  0.32701362,  0.91824786],
       [-0.85700833, -1.68269525,  0.646229  , -0.18337746],
       [-1.11288513, -1.49166212, -1.11482404, -0.11561882],
       [-0.44871305, -0.16365107, -1.23029491,  1.10665563],
       [-0.26786722,  0.09231292, -0.48023763, -0.80992272]])
In [20]:
df.describe()
Out[20]:
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean -0.224110 -0.571431 -0.571043 0.240165
std 0.856808 0.983304 0.898112 0.734900
min -1.112885 -1.682695 -1.574146 -0.809923
25% -0.754935 -1.371084 -1.201427 -0.166438
50% -0.358290 -0.586500 -0.797531 0.204694
75% -0.045669 0.028322 0.125201 0.819938
max 1.313419 0.826457 0.646229 1.106656
In [21]:
df.T
Out[21]:
2016-03-01 00:00:00 2016-03-02 00:00:00 2016-03-03 00:00:00 2016-03-04 00:00:00 2016-03-05 00:00:00 2016-03-06 00:00:00
A 1.313419 0.028397 -0.857008 -1.112885 -0.448713 -0.267867
B 0.826457 -1.009349 -1.682695 -1.491662 -0.163651 0.092313
C -1.574146 0.327014 0.646229 -1.114824 -1.230295 -0.480238
D 0.525008 0.918248 -0.183377 -0.115619 1.106656 -0.809923
In [22]:
df.T.shape
Out[22]:
(4, 6)
In [23]:
df.sort_index(axis=1, ascending=False)

# sort_index()按照索引排序
#df.sort_index() #按照rowID进行排序,默认升序
#df.sort_index(axis=1,ascending=False) #按照columnID进行排序,设定为降序
Out[23]:
D C B A
2016-03-01 0.525008 -1.574146 0.826457 1.313419
2016-03-02 0.918248 0.327014 -1.009349 0.028397
2016-03-03 -0.183377 0.646229 -1.682695 -0.857008
2016-03-04 -0.115619 -1.114824 -1.491662 -1.112885
2016-03-05 1.106656 -1.230295 -0.163651 -0.448713
2016-03-06 -0.809923 -0.480238 0.092313 -0.267867
In [24]:
df.sort_values(by='C')
# df.sort_values('mpg',ascending=False)
# Order rows by values of a column (high to low).
# 以每一列进行排序 ascending=False默认是降序,True就是升序
Out[24]:
A B C D
2016-03-01 1.313419 0.826457 -1.574146 0.525008
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377

数据选择

In [25]:
df['A']
# df[['A','B']] 取出两列
Out[25]:
2016-03-01    1.313419
2016-03-02    0.028397
2016-03-03   -0.857008
2016-03-04   -1.112885
2016-03-05   -0.448713
2016-03-06   -0.267867
Freq: D, Name: A, dtype: float64
In [26]:
df[2:4]
Out[26]:
A B C D
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
In [27]:
df['20160302':'20160305']
Out[27]:
A B C D
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656

通过标签选择

In [28]:
df.loc['20160301']
Out[28]:
A    1.313419
B    0.826457
C   -1.574146
D    0.525008
Name: 2016-03-01 00:00:00, dtype: float64
In [29]:
type(df.loc['20160301'])
Out[29]:
pandas.core.series.Series
In [30]:
df.loc[:, ['A', 'B']]
# 取出 AB两列
Out[30]:
A B
2016-03-01 1.313419 0.826457
2016-03-02 0.028397 -1.009349
2016-03-03 -0.857008 -1.682695
2016-03-04 -1.112885 -1.491662
2016-03-05 -0.448713 -0.163651
2016-03-06 -0.267867 0.092313
In [31]:
df.loc['20160301':'20160305', ['A', 'B']]

# 取出某几行 几列
Out[31]:
A B
2016-03-01 1.313419 0.826457
2016-03-02 0.028397 -1.009349
2016-03-03 -0.857008 -1.682695
2016-03-04 -1.112885 -1.491662
2016-03-05 -0.448713 -0.163651
In [32]:
df.loc['2016-03-01', 'A']
Out[32]:
1.3134192362700037
In [33]:
df.at[pd.Timestamp('2016-03-01'), 'A']
# df.at['2016-03-01', 'A'] will raise error
Out[33]:
1.3134192362700037

通过位置选择

In [34]:
df.iloc[1]
Out[34]:
A    0.028397
B   -1.009349
C    0.327014
D    0.918248
Name: 2016-03-02 00:00:00, dtype: float64
In [35]:
df.iloc[2:5, 0:2]
# 取出 2,3,4行,0,1,列
Out[35]:
A B
2016-03-03 -0.857008 -1.682695
2016-03-04 -1.112885 -1.491662
2016-03-05 -0.448713 -0.163651
In [36]:
df.iloc[1:5, :]
# df.iloc[1:5] 这个也可以
Out[36]:
A B C D
2016-03-02 0.028397 -1.009349 0.327014 0.918248
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
In [37]:
df.iloc[1, 1]
# 取出一行一列的值
Out[37]:
-1.009349292057921
In [38]:
df.iat[1, 1]
# 也可以达到同样的效果
Out[38]:
-1.009349292057921

布尔索引

In [39]:
df[df.A < 0]
# 取出 df.A小于0的值 筛选
Out[39]:
A B C D
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923
In [40]:
df[df > 0]
Out[40]:
A B C D
2016-03-01 1.313419 0.826457 NaN 0.525008
2016-03-02 0.028397 NaN 0.327014 0.918248
2016-03-03 NaN NaN 0.646229 NaN
2016-03-04 NaN NaN NaN NaN
2016-03-05 NaN NaN NaN 1.106656
2016-03-06 NaN 0.092313 NaN NaN
In [41]:
df['tag'] = ['a'] * 2 + ['b'] * 2 + ['c'] * 2
#添加一列
In [42]:
df
Out[42]:
A B C D tag
2016-03-01 1.313419 0.826457 -1.574146 0.525008 a
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c
In [43]:
df[df.tag.isin(['a', 'c'])]
# 筛选 通过 isin 
Out[43]:
A B C D tag
2016-03-01 1.313419 0.826457 -1.574146 0.525008 a
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c

修改数据

In [44]:
df
Out[44]:
A B C D tag
2016-03-01 1.313419 0.826457 -1.574146 0.525008 a
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c
In [45]:
s = pd.Series(np.arange(6), index=pd.date_range('20160301', periods=6))
s
Out[45]:
2016-03-01    0
2016-03-02    1
2016-03-03    2
2016-03-04    3
2016-03-05    4
2016-03-06    5
Freq: D, dtype: int64
In [46]:
df['E'] = s
In [47]:
df
Out[47]:
A B C D tag E
2016-03-01 1.313419 0.826457 -1.574146 0.525008 a 0
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a 1
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b 2
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b 3
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c 4
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c 5
In [48]:
df.loc['20160301', 'A'] = 0.2
# df.['20160301', 'A'] = 0.2 will not have effect
In [49]:
df
Out[49]:
A B C D tag E
2016-03-01 0.200000 0.826457 -1.574146 0.525008 a 0
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a 1
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b 2
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b 3
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c 4
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c 5
In [50]:
df.at[pd.Timestamp('20160301'), 'A'] = 0.4
In [51]:
df
Out[51]:
A B C D tag E
2016-03-01 0.400000 0.826457 -1.574146 0.525008 a 0
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a 1
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b 2
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b 3
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c 4
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c 5
In [52]:
df.iat[0, 0] = 0.6
df
Out[52]:
A B C D tag E
2016-03-01 0.600000 0.826457 -1.574146 0.525008 a 0
2016-03-02 0.028397 -1.009349 0.327014 0.918248 a 1
2016-03-03 -0.857008 -1.682695 0.646229 -0.183377 b 2
2016-03-04 -1.112885 -1.491662 -1.114824 -0.115619 b 3
2016-03-05 -0.448713 -0.163651 -1.230295 1.106656 c 4
2016-03-06 -0.267867 0.092313 -0.480238 -0.809923 c 5
In [53]:
df.loc[:, 'A'] = np.arange(10, 16)
df
Out[53]:
A B C D tag E
2016-03-01 10 0.826457 -1.574146 0.525008 a 0
2016-03-02 11 -1.009349 0.327014 0.918248 a 1
2016-03-03 12 -1.682695 0.646229 -0.183377 b 2
2016-03-04 13 -1.491662 -1.114824 -0.115619 b 3
2016-03-05 14 -0.163651 -1.230295 1.106656 c 4
2016-03-06 15 0.092313 -0.480238 -0.809923 c 5
In [54]:
df2 = df.loc[:, ['B', 'C']].copy()
df2[df2 > 0] = -df2
df2
Out[54]:
B C
2016-03-01 -0.826457 -1.574146
2016-03-02 -1.009349 -0.327014
2016-03-03 -1.682695 -0.646229
2016-03-04 -1.491662 -1.114824
2016-03-05 -0.163651 -1.230295
2016-03-06 -0.092313 -0.480238

[亲测]python中pandas库中DataFrame对行和列的操作使用方法

用pandas中的DataFrame时选取行或列:
import numpy as np
import pandas as pd
from pandas import Sereis, DataFrame

ser = Series(np.arange(3.))

data = DataFrame(np.arange(16).reshape(4,4),index=list('abcd'),columns=list('wxyz'))

data['w']  #选择表格中的'w'列,使用类字典属性,返回的是Series类型

data.w    #选择表格中的'w'列,使用点属性,返回的是Series类型

data[['w']]  #选择表格中的'w'列,返回的是DataFrame类型

data[['w','z']]  #选择表格中的'w'、'z'列

data[0:2]  #返回第1行到第2行的所有行,前闭后开,包括前不包括后

data[1:2]  #返回第2行,从0计,返回的是单行,通过有前后值的索引形式,
       #如果采用data[1]则报错

data.ix[1:2] #返回第2行的第三种方法,返回的是DataFrame,跟data[1:2]同

data['a':'b']  #利用index值进行切片,返回的是**前闭后闭**的DataFrame, 
        #即末端是包含的  
data.irow(0)   #取data的第一行
data.icol(0)   #取data的第一列

data.head()  #返回data的前几行数据,默认为前五行,需要前十行则data.head(10)
data.tail()  #返回data的后几行数据,默认为后五行,需要后十行则data.tail(10)

ser.iget_value(0)  #选取ser序列中的第一个
ser.iget_value(-1) #选取ser序列中的最后一个,这种轴索引包含索引器的series不能采用ser[-1]去获取最后一个,这会引起歧义。

data.iloc[-1]   #选取DataFrame最后一行,返回的是Series
data.iloc[-1:]   #选取DataFrame最后一行,返回的是DataFrame

data.loc['a',['w','x']]   #返回‘a’行'w'、'x'列,这种用于选取行索引列索引已知

data.iat[1,1]   #选取第二行第二列,用于已知行、列位置的选取。
下面是简单的例子使用验证:
import pandas as pd
from pandas import Series, DataFrame
import numpy as np

data = DataFrame(np.arange(15).reshape(3,5),index=['one','two','three'],columns=['a','b','c','d','e'])

data
Out[7]: 
        a   b   c   d   e
one     0   1   2   3   4
two     5   6   7   8   9
three  10  11  12  13  14

#对列的操作方法有如下几种

data.icol(0)   #选取第一列
E:\Anaconda2\lib\site-packages\spyder\utils\ipython\start_kernel.py:1: FutureWarning: icol(i) is deprecated. Please use .iloc[:,i]
  # -*- coding: utf-8 -*-
Out[35]: 
one       0
two       5
three    10
Name: a, dtype: int32

data['a']
Out[8]: 
one       0
two       5
three    10
Name: a, dtype: int32

data.a
Out[9]: 
one       0
two       5
three    10
Name: a, dtype: int32

data[['a']]
Out[10]: 
        a
one     0
two     5
three  10

data.ix[:,[0,1,2]]  #不知道列名只知道列的位置时
Out[13]: 
        a   b   c
one     0   1   2
two     5   6   7
three  10  11  12

data.ix[1,[0]]  #选择第2行第1列的值
Out[14]: 
a    5
Name: two, dtype: int32

data.ix[[1,2],[0]]   #选择第2,3行第1列的值
Out[15]: 
        a
two     5
three  10

data.ix[1:3,[0,2]]  #选择第2-4行第1、3列的值
Out[17]: 
        a   c
two     5   7
three  10  12

data.ix[1:2,2:4]  #选择第2-3行,3-5(不包括5)列的值
Out[29]: 
     c  d
two  7  8

data.ix[data.a>5,3]
Out[30]: 
three    13
Name: d, dtype: int32

data.ix[data.b>6,3:4]  #选择'b'列中大于6所在的行中的第4列,有点拗口
Out[31]: 
        d
three  13

data.ix[data.a>5,2:4]  #选择'a'列中大于5所在的行中的第3-5(不包括5)列
Out[32]: 
        c   d
three  12  13

data.ix[data.a>5,[2,2,2]]  #选择'a'列中大于5所在的行中的第2列并重复3次
Out[33]: 
        c   c   c
three  12  12  12

#还可以行数或列数跟行名列名混着用
data.ix[1:3,['a','e']]
Out[24]: 
        a   e
two     5   9
three  10  14

data.ix['one':'two',[2,1]]
Out[25]: 
     c  b
one  2  1
two  7  6

data.ix[['one','three'],[2,2]]
Out[26]: 
        c   c
one     2   2
three  12  12

data.ix['one':'three',['a','c']]
Out[27]: 
        a   c
one     0   2
two     5   7
three  10  12

data.ix[['one','one'],['a','e','d','d','d']]
Out[28]: 
     a  e  d  d  d
one  0  4  3  3  3
one  0  4  3  3  3

#对行的操作有如下几种:
data[1:2]  #(不知道列索引时)选择第2行,不能用data[1],可以用data.ix[1]
Out[18]: 
     a  b  c  d  e
two  5  6  7  8  9

data.irow(1)   #选取第二行
Out[36]: 
a    5
b    6
c    7
d    8
e    9
Name: two, dtype: int32

data.ix[1]   #选择第2行
Out[20]: 
a    5
b    6
c    7
d    8
e    9
Name: two, dtype: int32


data['one':'two']  #当用已知的行索引时为前闭后闭区间,这点与切片稍有不同。
Out[22]: 
     a  b  c  d  e
one  0  1  2  3  4
two  5  6  7  8  9

data.ix[1:3]  #选择第2到4行,不包括第4行,即前闭后开区间。
Out[23]: 
        a   b   c   d   e
two     5   6   7   8   9
three  10  11  12  13  14

data.ix[-1:]  #取DataFrame中最后一行,返回的是DataFrame类型,**注意**这种取法是有使用条件的,只有当行索引不是数字索引时才可以使用,否则可以选用`data[-1:]`--返回DataFrame类型或`data.irow(-1)`--返回Series类型
Out[11]: 
        a   b   c   d   e
three  10  11  12  13  14

data[-1:]  #跟上面一样,取DataFrame中最后一行,返回的是DataFrame类型
Out[12]: 
        a   b   c   d   e
three  10  11  12  13  14

data.ix[-1] #取DataFrame中最后一行,返回的是Series类型,这个一样,行索引不能是数字时才可以使用
Out[13]: 
a    10
b    11
c    12
d    13
e    14
Name: three, dtype: int32

data.tail(1)   #返回DataFrame中的最后一行
data.head(1)   #返回DataFrame中的第一行
最近处理数据时发现当pd.read_csv()数据时有时候会有读取到未命名的列,且该列也用不到,一般是索引列被换掉后导致的,有强迫症的看着难受,这时候dataframe.drop([columns,])是没法处理的,怎么办呢, 
最笨的方法是直接给列索引重命名:

data6

        Unnamed: 0  high    symbol  time
date                
2016-11-01  0   3317.4  IF1611  18:10:44.8
2016-11-01  1   3317.4  IF1611  06:01:04.5
2016-11-01  2   3317.4  IF1611  07:46:25.5
2016-11-01  3   3318.4  IF1611  09:30:04.0
2016-11-01  4   3321.8  IF1611  09:31:04.0

data6.columns = list('abcd')

data6

    a   b   c   d
date                
2016-11-01  0   3317.4  IF1611  18:10:44.8
2016-11-01  1   3317.4  IF1611  06:01:04.5
2016-11-01  2   3317.4  IF1611  07:46:25.5
2016-11-01  3   3318.4  IF1611  09:30:04.0
2016-11-01  4   3321.8  IF1611  09:31:04.0
重新命名后就可以用dataframe.drop([columns])来删除了,当然不用我这样全部给列名替换掉了,可以只是改变未命名的那个列,然后删除。不过这个用起来总是觉得有点low,有没有更好的方法呢,有,可以不去删除,直接:

data7 = data6.ix[:,1:]
这样既不改变原有数据,也达到了删除神烦列,当然我这里时第0列删除,可以根据实际选择所在的列删除之,至于这个原理,可以看下前面的对列的操作。

NumPy基本操作,常用函数

内容索引

该小结主要介绍了NumPy数组的基本操作。

子目1中,介绍创建和索引数组,数据类型,dtype类,自定义异构数据类型。

子目2中,介绍数组的索引和切片,主要是对[]运算符的操作。

子目3中,介绍如何改变数组的维度,分别介绍了ravel函数、flatten函数、transpose函数、resize函数、reshape函数的用法。

In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

ndarray是一个多维数组对象,该对象由实际的数据、描述这些数据的元数据组成,大部分数组操作仅仅修改元数据部分,而不改变底层的实际数据。

用arange函数创建数组

In [2]:
a = arange(5)
#生成5个元素的数组
a.dtype
#显示a的类型
Out[2]:
dtype('int64')
In [3]:
a
Out[3]:
array([0, 1, 2, 3, 4])
In [4]:
a.shape
#获取数组内是几行几列
Out[4]:
(5,)

数组的shape属性返回一个元祖(tuple),元组中的元素即NumPy数组每一个维度的大小。

1. 创建多维数组

array函数可以依据给定的对象生成数组。 给定的对象应是类数组,如python的列表、numpy的arange函数

In [5]:
m = array([arange(2), arange(2)])

生成一个两行两列的数组
In [6]:
print m
print m.shape
print type(m)
print type(m.shape)
[[0 1]
 [0 1]]
(2, 2)
<type 'numpy.ndarray'>
<type 'tuple'>

选取元素

In [7]:
a = array([[1,2],[3,4]])
print a[0,0]
print a[0,1]
1
2

NumPy数据类型

Numpy除了Python支持的整型、浮点型、复数型之外,还添加了很多其他的数据类型。

Type Remarks Character code bool_ compatible: Python bool ‘?’ bool8 8 bits
Integers:

byte compatible: C char ‘b’ short compatible: C short ‘h’ intc compatible: C int ‘i’ int_ compatible: Python int ‘l’ longlong compatible: C long long ‘q’ intp large enough to fit a pointer ‘p’ int8 8 bits
int16 16 bits
int32 32 bits
int64 64 bits
Unsigned integers:

ubyte compatible: C unsigned char ‘B’ ushort compatible: C unsigned short ‘H’ uintc compatible: C unsigned int ‘I’ uint compatible: Python int ‘L’ ulonglong compatible: C long long ‘Q’ uintp large enough to fit a pointer ‘P’ uint8 8 bits
uint16 16 bits
uint32 32 bits
uint64 64 bits
Floating-point numbers:

half ‘e’ single compatible: C float ‘f’ double compatible: C double
float_ compatible: Python float ‘d’ longfloat compatible: C long float ‘g’ float16 16 bits
float32 32 bits
float64 64 bits
float96 96 bits, platform?
float128 128 bits, platform?
Complex floating-point numbers:

csingle ‘F’ complex_ compatible: Python complex ‘D’ clongfloat ‘G’ complex64 two 32-bit floats
complex128 two 64-bit floats
complex192 two 96-bit floats, platform?
complex256 two 128-bit floats, platform?
Any Python object:

object_ any Python object ‘O’

每一种数据类型均有对应的类型转换函数

In [8]:
print float64(42)
print int8(42.0)
print bool(42)
print float(True)
42.0
42
True
1.0
In [9]:
arange(8, dtype=uint16)
Out[9]:
array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint16)

复数不能转换成整数和浮点数

Numpy数组中每一个元素均为相同的数据类型,现在给出单个元素所占字节

In [10]:
a.dtype
Out[10]:
dtype('int32')
In [11]:
a.dtype.itemsize
Out[11]:
4

dtype类的属性

In [12]:
t = dtype('float64')
print t.char
print t.type
print t.str
d
<type 'numpy.float64'>
<f8

str属性可以给出数据类型的字符串表示,该字符串的首个字符表示字节序,然后是字符编码,然后是所占字节数 字节序是指位长为32和64的字(word)存储的顺序,包括大端序(big-endian)和小端序(little-endian)。 大端序是将最高位字节存储在最低的内存地址处,用>表示;与之相反,小端序是将最低位字节存储在最低的内存地址处,用<表示。

创建自定义数据类型

自定义数据类型是一种异构数据类型,可以当做用来记录电子表格或数据库中一行数据的结构。

下面我们创建一种自定义的异构数据类型,该数据类型包括一个用字符串记录的名字、一个用整数记录的数字以及一个用浮点数记录的价格。

In [13]:
t = dtype([('name', str_, 40), ('numitems', int32), ('price', float32)])
In [14]:
t
Out[14]:
dtype([('name', 'S40'), ('numitems', '<i4'), ('price', '<f4')])
In [15]:
t['name']
Out[15]:
dtype('S40')
In [16]:
itemz = array([('Meaning of life DVD', 32, 3.14), ('Butter', 13, 2.72)], dtype=t)
In [17]:
itemz[1]
Out[17]:
('Butter', 13, 2.7200000286102295)

2. 数组的索引和切片

In [18]:
a = arange(9)
#下标0-7, 以2为步长
print a[:7:2]

#以负数下标翻转数组
print a[::-1]
Out[21]: array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
print a[::-2]
Out[22]: array([9, 7, 5, 3, 1])

多维数组的切片和索引

In [19]:
b = arange(24).reshape(2,3,4)
print b.shape
print b
(2, 3, 4)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

用三维坐标选定任意一个房间,即楼层、行号、列号

In [20]:
#选取第一层楼所有房间
print b[0]
print
print b[0, :, :]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
In [21]:
#多个冒号用一个省略号代替
b[0, ...]
Out[21]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
In [22]:
#间隔选元素
b[0,1,::2]
一层楼,第一个房间 步长是2
Out[22]:
array([4, 6])
In [23]:
#多维数组执行翻转一维数组的命令,将在最前面的维度上翻转元素的顺序
b[::-1]
Out[23]:
array([[[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]],

       [[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]]])
In [24]:
b[::-1,::-1,::-1]
Out[24]:
array([[[23, 22, 21, 20],
        [19, 18, 17, 16],
        [15, 14, 13, 12]],

       [[11, 10,  9,  8],
        [ 7,  6,  5,  4],
        [ 3,  2,  1,  0]]])

3. 改变数组的维度

ravel 完成展平操作

In [25]:
b.ravel()
#所有的列表转成一个列表
Out[25]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])

flatten 也是展平

flatten函数会请求分配内存来保存结果,而ravel函数只是返回数组的一个视图(view)

In [26]:
b.flatten()
Out[26]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])

用元组设置维度

In [27]:
b.shape = (6, 4)
In [28]:
b
Out[28]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])

transpose转置矩阵

In [29]:
b.transpose()
Out[29]:
array([[ 0,  4,  8, 12, 16, 20],
       [ 1,  5,  9, 13, 17, 21],
       [ 2,  6, 10, 14, 18, 22],
       [ 3,  7, 11, 15, 19, 23]])

resize和reshape函数功能一样 但resize会直接改变所操作的数组

In [30]:
b.reshape(2,3,4)
Out[30]:
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
In [31]:
b
Out[31]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
In [32]:
b.resize(2,12)
In [33]:
b
Out[33]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]])

IPython 教程,IPython技巧,开始使用IPython

在本章中,我们首先进行一遍IPython的安装过程,再大概浏览一下IPython提供的功能。IPython提供了一个高度优化的Python控制台和Notebook。除此之外,当与第三方包,如NumPyMatplotlib,结合使用进行交互式计算时它是不可或缺的工具。这些包给Python提供了高性能计算和交互式可视化的特性。在本章的结尾,你将安装好IPython及其相关的依赖包在电脑上。你也将了解了IPython主要功能,这些功能将会在后续的章节详细介绍。

这些细节包括:

  • ①运行IPython控制台
  • ②使用IPython作为系统shell
  • ③使用历史(history)
  • ④Tab补全
  • ⑤使用%run命令运行脚本
  • ⑥使用%timeit命令快速测量时间
  • ⑦使用%pdb命令快速debug
  • ⑧使用Pylab进行交互式计算
  • ⑨使用IPython Notebook
  • ⑩定制IPython

安装IPython及推荐的包

不要问为什么,我们使用Python2.7版本,直接安装Anaconda集成包,我们所需要的东西里面都有,且各种依赖的版本相协调,自己逐个安装的话将会很麻烦,尤其是遇到版本不匹配的时候。

这些包主要是 :

  • NumPy 高性能多维数组矢量计算
  • SciPy 高级i数值计算算法
  • Matplotlib 绘图及交互式可视化
  • Matplotlib-basemap Matplotlib绘图工具箱
  • Network X 处理图问题
  • Pandas 处理任意的表数据
  • Python Image Library(PIL),图片处理算法
  • PySide Qt的Python封包,用于图形用户接口(GUI)
  • PyQt 和PySide类似,但使用不同的用户协议
  • Cython 在Python中使用C语言代码

【自己逐个安装的话注意一下版本号】

使用Python的包系统

主要是下面两个,学过Python应该会对其比较熟悉。不了解的可以复制为关键字通过搜索引擎寻找答案

  • ①easy_install
  • ②pip

可选的IPython依赖

  • ①pyreadline 提供行编辑功能
  • ②pyzmq IPython并行计算依赖,如Qt控制台,Notebook
  • ③pygments Qt控制台语法高亮
  • ④tornado 基于web的Botebook的依赖

注意:在你安装二进制IPython的时候它们将自动安装,而从源码安装却不会。

IPython的十大基础

运行IPython控制台

如果IPython已经被正确安装,你就应该能够从系统shell中使用ipython命令启动它。你可以使用这个代理替代Python解释器,如下面截图这样

当然,IPython能提供的不止这个。IPython提供了数十个小命令可以用来提高工作效率。我们可以在本书中见到很多。

这些命令中有些用来帮助你获得任意的Python函数和对象的心意。比如,你是否遇到过对怎样在派生类中使用super函数访问父类的方法?只需敲下super%pinfo super 的快捷方式) 你就会找到与super函数相关的所有信息。appending?或??等等,所有的命令或变量你都能获得他们的信息。使用示例如下:

In [1]: super?
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)

使用 IPython作为系统shell

你可以使用IPython的命令行接口作为一个扩展的系统shell。你可以使用它访问你的文件系统和执行任何系统命令。比如,标准的Unix命令pwd、ls和cd在IPython中都是可用的,在windows平台下也能使用。就像下面这样:

In [1]: pwd
Out[1]: u'C:\\'
In [2]: cd windows
C:\windows

这些命令尤其是“魔法命令”是IPython的核心。在本书中我们将会使用数十个魔法命令。你可以通过魔法命令%lsmagic获得所有魔法命令的列表。

【魔法命令实际上带有一个%prefix,但是自动魔法系统缺省状态下是开启的,你可以直接忽略掉它。这个prefix是随时可用的,尤其是当一个unprefixed命令被一个同名的Python变量所掩盖的时候。%automagic命令可以开启自动魔法系统。在本书中,我们一般使用%prefix去引用魔法命令。但是请记住,如果你喜欢,完全可以忽略不用它】

使用history

就像标准的控制台一样,IPython提供历史命令的功能。然而,不同于Python控制台,IPython的命令历史扩展了对历史命令的交互会话。除此之外,很多关键字缩写可以大大减少重复的输入。

在IPython控制台代理中,使用上下键浏览所有的历史输入。如果在按下箭头键之前开始输入,只有你匹配显示你曾经输入的命令。

在任何交互会话中,你的所有输入和输出历史将会被保存在In和Out变量中,并被编号进行索引。_,__,____i,__i,___i变量保存着 最后三个输出和输入对象。_n_in变量返回第n个输出和输入历史命令。比如,让我们尝试一下

In [4]: a = 12
In [5]: a ** 2
Out[5]: 144
In [6]: print("The result is {0:d}.".format(_))
The result is 144.

在这个示例中,我们在第六行显示了输出,就是第五行的输出

Tab补全

Tab补全是非常有用的一个功能,你将会发现你会经常使用它。无论何时你开始如入任何命令、变量名或函数,敲击Tab键让IPython自动帮你补全你想要输入的内容或显示一个可能匹配的命令或变量名列表。对于文件目录也是有效的,就像系统shell一样。

你也可以使用它进行动态对象提示。输入任何Python对象之后紧跟这输入一个点,按下Tab键,IPython将会显示一个存在的属性和方法列表,就像下面这样:

n [1]: import os
In [2]: os.path.split<TAB>
os.path.split os.path.splitdrive os.path.splitext os.path.splitunc

在第二行,就像先前的代码一样,在os.path.split之后敲击Tab键,IPython接着就会显示可能的命令。

【Tab补全和私有变量:Tab补全只会显示除以下划线开头之外的对象属性和方法。原因是在Python中以下划线开头的属性和方法是私有的。若要强制IPython显示所有的私有属性和方法,在敲击Tab键之前输入myobject._。在Python中没有真正的私有和隐藏。这是Python哲学的一部分,就像一据挺有名的话所表达的那样“大家都是成年人了”】

使用%run命令运行脚本

尽管很重要,交互式控制台在运行多命令序列时受到很大限制。编写多条命令脚本,以.py后缀保存文件,这样的Python脚本可以在IPython中以 %run+文件名的形式进行运行。除非-i选项被使用,脚本将运行在一个全新的命名空间。但脚本中定义的所有变量在脚本执行结束后都成为可用的。

让我们编写下面的Python脚本并以script.py保存:

print("Running script.")
x = 12
print("'x' is now equal to {0:d}.".format(x))

假设我们现在在文件所在的目录,我们可以在IPython中像下面这样运行它:

In [1]: %run script.py
Running script.
'x' is now equal to 12.
In [2]: x
Out[2]: 12

使用%timeit命令快速测量代码运行时间

在一个交互会话中你可以使用%timeit魔法命令快速测量代码运行时间。这可以让你评估单挑命令的运行时长。相同的命令会在一个循环中多次执行,多次运行时长的平均值作为该命令的最终评估时长。-n选项控制命令在单次循环中执行的次数,-r选项控制执行循环的次数。例如

In[1]: %timeit [x*x for x in range(100000)]
10 loops, best of 3: 26.1 ms per loop

看,当x达到100000的时候花费了26毫秒。

使用%debug命令进行快速debug

IPython带有一个强大的调试器。无论何时控制台抛出了一个异常,你都可以使用%debug魔法命令在异常点启动调试器。接着你就能调试模式下访问所有的本地变量和整个栈回溯。使u和d向上向下访问栈,使用q推出调试器。在调试器中输入?可以查看所有的可用命令列表。

你也可以使用%pdb魔法命令去激活IPython调试器,这样,每当异常抛出时,调试器就会自动运行。

使用Pylab进行交互式计算

%pylab魔法命令可以使Numpymatplotlib中的科学及算功能生效,这些功能被称为基于向量和矩阵的高效操作,交互式可视化特性。这使得在控制台进行交互式计算和动态绘图称为可能。如:

In [1]: %pylab
Welcome to pylab, a matplotlib-based Python environment [backend: TkAgg].
For more information, type 'help(pylab)'.
In [2]: x = linspace(-10., 10., 1000)
In [3]: plot(x, sin(x))

在这个示例中,我们首先定义了一个-10到10线性空间1000个数值的向量。接着我们绘制(x,sin(x))。像下面的截图一样,一个带有绘制图线的窗口就会出现。而且在窗口被打开时,控制台不会被阻塞。这就允许我们可以在窗口打开的状态下动态的修改绘制的图线。

使用IPython Notebook

Notebook将IPython的功能带到了浏览器中,可以多行编辑、交互会话再现等等。它是一种在交互式使用Python的现代的、强大的方式。

要使用Notebook,只需在shell中输入ipython notebook这个命令(需确认你已经正确安装了在安装章节提到的各种依赖)。接着就会在默认的8888断开开启一个本地的web服务。在浏览器中打开http://127.0.0.1:8888/创建一个新的Notebook

你可以在输入框内编写多行代码。这里是一些常用的快捷键:

  • ①Enter 在输入框内创建新的一行且不执行这个输入框中的代码;
  • ②Shift+Enter 执行这个框内的代码并转到下一个框内;
  • ③Alt+Enter 执行框内代码,并在之后追加一个空的输入框
  • ④Ctrl+Enter 当你不想保存输出时,进行快速的实例实验;
  • ⑤Ctrl+M 接着K 显示所有的键盘快捷键。

我们将会在后续的章节详细介绍Notebook的特性功能。

定制IPython

你可以用一个Python文件保存你的用户配置;这个文件被称作IPython配置文件。在shell中输入ipython profile create 来创建默认的配置文件。这将会在.~/.ipython或~/.config/ipython目录下创建一个明文profile_config.py的文件夹。你可以使用ipython profile create profilename以不同的名字创建配置文件,接着以ipython --profile=profilename使用配置文件启动IPython。

~目录是你的家目录,例如,在Unix中/home/yourname,或者windows中c:\users\yourname、c:\documents and settings\yourname。

总结

在这一章中,我们已经学习了IPthon及其扩展Python包的多种安装方式。最直接的方式是安装一个包含所有包的独立Python发行版如Enthought Python Distribution or CanopyAnacondaPython(x,y), 或ActivePython。另外的一种方式是逐步以二进制形式或Python的包系统形式安装不同的包。

我们还大概浏览了IPyton提供的十大有趣特性功能。他们对于Python的交互式操作有很大联系,包括集成调试器和配置文件、Numpymatplotlib提供的交互式计算和可视化。在后续的章节中,我们将会详细学习交互式shell、Python控制台以及Notebook。

pandas 读写excle中的多个表

import pandas as pd
import numpy as np
xls = pd.ExcelFile('aa.xlsx')
df = xls.parse('Sheet1')
df2 = xls.parse('Sheet2')

xls.sheet_names  # see all sheet names

xls.parse(sheet_name)  # read a specific sheet to DataFrame
df.to_csv('cc.csv',encoding=('utf-8')) #写入csv中