基于python2

scrapy是一款非常轻量级的爬虫框架,但是由于它隐藏了太多关于网络请求的细节,所以我们有时候会遭遇到一下很尴尬的bug,当然这主要是因为碰到一些不规范的网站。

python的编码转码网上有很多文章,如果你不了解这个你可以参考下面了解。

Ned Batchelder 关于python unicode和str的理解,通俗易懂

关于scrapy 入门

关于 encode的认识

通过上面我们可以很好的理解python的转码译码,在这里我想谈一下我自己对其的认识吧,我一开始接触的c语言序列的基本上都是强类型,比如C里面假如我想写一个函数每个传人的参数都得是有类型的,但是python弱化了类型这一点,python也是面对对象的,但是他的对象就是鸡同鸭讲,照猫画虎就能运行,弱类型适合动态语言,我们不确定下一行代码输入的是什么,自从学python起,一直感觉python对类型一直不严格,这样就给了我一种错觉,只要长得差不多就能一样的比划,比如在两个string,'中国',u'中国',看起来差不多但是如果你把u'中国'存入文件中就会出错(假如你没定义编码规则)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 344-351: ordinal not in range(128)
unicode字符编码错误,要想理解这个要对unicode字符集和unicode编码有一定的理解,推荐你读一下这篇博客字符编码的知识,python内部使用unicode字符集存贮所以的编码的字符,为什么要用unicode字符集举个栗子吧:

A是米国的程序员,他使用asicc编码的文件上传了一封邮件,
B是中国的程序员他使用gbk编码的文件上传了一封邮件,
现在C要用程序同时处理A和B的邮件,有两种解决方法他把A的文件译码再编码成B的gbk,或者将B的文件译码成asicc但是中文无法处理,那么只能使用第一种方法将A的文件编码成gbk,但是改天D又来啦,他是俄国人,天啊噜gbk可能没有把俄语编进去,那肿么办,我们迫切需要一种编码可以把所以的字符放进去,所以unicode出现了,Unicode中将字符集按照一定的类别划分到0~16这17个层面(Planes)中,每个层面中拥有216=65536个字符码,因此Unicode总共拥有的字符码,也即是Unicode的字符空间总共有17*65536=1114112,一共有1114112这么多的字符可以用,这下我们不用担心了吧,太好了这下不用愁了,

python 内部使用unicode字符集作为一个译码中转站,因为他编码了所以的字符集,只要你能在自己编码方案上找到自己的字,我就能在unicode字符集找到你的位置,所以使用unicode可以很好的解决多种编码方案产生的问题(比如gbk,utf-8)
当然其他编码方案如果想使用unicode解码成其他的必须同unicode有一一对应关系,不过现在主流的编码方案如gbk,gb2312,utf-8都是unicode系的。

了解了这些基础知识就可以知道了为什么存贮u'中国'存不进文件里面去了,因为unicode并不提供给当今字符解析器的方法,就是\u234e一个16进制数字,屏幕上不知道他对应什么图形,所以python系统要求存进文件的必须是字节流,也就是可以unicode是一种更高级的字符流,这个字符流能存贮当今世界所以定义的字符,但是他只是一个规定字符集合,我们只需要把发现的字符放进去占据一个位置,但是我们不需要考虑屏幕是否认识这个字符,这个字符的存贮由编码方案负责,如utf-8这些,假如没有字符编码方案可以存贮这些,我们虽然在unicode上有这个字符但是我们无法print出来,所以我们必须将unicode转换成普通字符流,有人就会问了,假如我真的没有找到一个合适编码方案可以存贮所有语言,我们可以将他编码成unicode—escape类型,这里我们不多讲。

这就可以解释我们大部分碰到的错误unicodedecodeerror和unicodeencodeerror错误,都是因为字符编码方案不了解造成的,网上很多说碰到这种错误就encode,decode搞一下就行但是不弄清楚这背后的知识就会犯迷糊。

接下来我谈谈我遇到的错误吧,在爬取http://yjsy.ncu.edu.cn/yjs_showmsg.asp?id=2770这个页面时(这是一个不规范的页面没有设置charset),因为每个spider调用的

response.xpath('//xpath').extract()    

选择器返回的是一个unicode编码的字符集,但是他是接受的是一个字符流,spider可能调用了
response.body.decode(response.encoding)进行转码,但是这个response.encoding有时候会判断错误,比如将我一个gbk编码的文件判断成cp1253,这个时候假如我把他解码成encode成其他编码方式的话,我们就会得到乱码,那怎么纠正呢,我们可以这样干
先将得到的列表中每个content取出来,然后使用content.encode(resonse.encoding)转码成原始字符流,现在你可以将它用正确的编码转换成unicode了

下面是我github上的关于这个scrapy的项目,在coding_pitch.py文件里面就是对于这个乱码的处理

南昌大学教务处公告爬取

最近在学利用python进行数据分析,发现pandas这个库对数据的处理非常灵活,
而且号称python的maltlab的.其在图形绘制也非常方便

在Windows下安装python关于科学计算的库非常不方便,还好有一些公司做好了关于python计算库在Windows安装的安装文件,这里我推荐anaconda,我推荐安装32位包,因为64位有很多库不支持.

pandas内部是使用numpy做存贮结构的,所以我们必须先了解一下numpy

下面是numpy的简析

关于pandas的DataFrame和Series的理解


string

原来的很多函数都逐渐迁移到strunicode对象上去了,
不过有两个函数没有迁移出去


第一个是capwords

个人觉得没什么卵用
就是将英文单词首字母大写

比如

string.capwords('this are some words')

输出为 This Are Some Words


第二个我觉得挺有趣,他可以帮你把对应的文字换成你设定的

我们可以用它来设计出莫斯密码’=’.

"""create a table"""
table = string.maketrans('abc','123')
print 'abc123'.translate(table)

输出为123123成功把abc转成了123


string还有一个模板类型Template
这个类型同我们转义差不多(%),不过能够实现的
更加自主化
我们可以继承这个类来修改模板类的具体实现
而且这个模板类有一个


textwrap

由于sublime输出一个很长的字符串很卡,这个格式化字符串的类能够帮我们
解决很多问题

我一般去除整体缩进然后去填充字符串

dedented_text = textwrap.dedent('...a long word...')
print textwrap.fill(dedented_text, width=50)


由于网站要实现图片和文字的混排的上传
所以在网上找了富文本编辑器的插件,发现CKEditor这款还不错的插件

我用的是4.5.1这个版本,的确很好用,只是引用了一个js文件就可以实现。


  1. 将网上下的CKEditor包解压放在根目录下
  2. 在页面上引用CKEditor的核心包ckeditor.js
<script src="ckeditor/ckeditor.js" type="text/javascript"></script>;
  1. 在页面添加一个输入框textarea

    <textarea name="individual" id="individual" runat="server"></textarea>
    <script type="text/javascript">
     CKEDITOR.replace('individual');
     </script>
    

只要通过上面上面几个步骤就能实现富文本编辑器,但是点开图片上传功能,发现只有上传url的功能,并不能本地上传图片,百度了一下发现由于安全性问题CKEditor没有上传功能,只有安上CKFinder才能实现上传功能,于是我在官网下了ckfinder__aspnet_2.5.0.1,同CKEditor一样引用JS文件(只要引用ckfinder.js)

<script src="ckfinder/ckfinder.js" type="text/javascript"></script>
  • 由于我是MVC的网站基于.net4.5,在官方给的包里面有一个asp.net网站实例放在_source文件夹里面,里面有一个基于.net2的示例网站,一运行就报找不到 System.Web.UI.Design这个命名空间的错,所以我把它从项目中排除,把项目中bin中debug文件夹下的CKFinder.dll复制出来,引用到我自己的项目中。

    接下来要配置CKEditor来让CKFinder引用进来,在CKEditor文件夹下config.js在CKEDITOR.editorConfig = function (config) {};方法中添加如下代码:

    config.filebrowserImageBrowseUrl = ‘ckfinder/ckfinder.html?Type=Images’;
    config.filebrowserFlashBrowseUrl = ‘ckfinder/ckfinder.html?Type=Flash’;
    config.filebrowserUploadUrl = 'ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files';
    
    config.filebrowserImageUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images’;
    config.filebrowserFlashUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash’;
    config.filebrowserWindowWidth = ‘800’; //“浏览服务器”弹出框的size设置
    config.filebrowserWindowHeight = ‘500’;

注意在配置Url的时候要修改成相对于网站本地网站磁盘文件路径,比如说,你的网址是http://example.com.cn ,你把ckfinder文件夹放在Admin下的Editor文件夹,那么所有url要改成下面类似的格式

config.filebrowserImageBrowseUrl ='/Admin/Editor/ckfinder/ckfinder.html?Type=Images';

否则会报404错误,

最后一步是修改一个函数让所有人能看到服务器上传文件夹里面的文件,在ckfinder文件夹下面的cofig.ascx文件,找到 CheckAuthentication函数将返回值改成true

当然如果你想修改上传文件的地址,你可以在上面方法里面找到SetConfig()方法,找到BaseUrl,修改为你想上传的地址,

—–

PS:

我是在在VS里面进行调试的,由于VS的IIS在调试的时候不允许对磁盘文件的路由地址访问,就是CKFinder通过ckfinder.html这个html来实现上传图片的功能,但是这个在调试的时候VS无法访问这个文件,所以一直报404错误,可以修改IIS来允许IIS访问磁盘文件,步骤如下:

  1. 右键点击IIS Express,选择显示所有应用程序
  2. 找到运行网站的配置,进入applicationhost.config文件夹
  3. ctrl+f 寻找UrlRoutingModule
  4. 将preCodition设置为空字符

    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
    

如果你是用MVC进行表单传值的话,你必须在post方法上面添加 [ValidateInput(false)] 属性,如果不这样的话就会报下面的错

“/”应用程序中的服务器错误。
从客户端(content="<p>sdfsdafwewo shdfh...")中检测到有潜在危险的 Request.Form 值。

由于有其他编程语言基础,所以对于python的学习并不吃力,但是整体感觉python的确与前面学习c、c———

1. 实时编译VS静态编译

不需要输入任何前缀,直接将代码放在python解释器上面就能运行,虽然window下不支持直接点开文件就能使用,但是只要安装了python解释器就能很轻松的运行。

分量轻是他的特点吧!相比打开vs等半天然后,编译连接最后执行。python是一门很轻巧的语言,没有满屏的分号,大括号,基本类型比如int、string、float不区分直接拿来用就可以了,任何一个变量都是一个对象,对象可以千变万化,感觉python是一门很野的熊孩子什么都不在乎,比如说你什么了相同的两个变量

这门强类型语言则不允许,在同级作用域内他只允许声明一次,python或许已经没有声明了,每个名字只是一个对象而已并没有他的归属。

2.动态语言VS静态语言

给我感受是一颗静止的树的话,那么python就是一匹‘野马’,C

最近在学利用python进行数据分析,发现pandas这个库对数据的处理非常灵活,
而且号称python的maltlab的.其在图形绘制也非常方便

在Windows下安装python关于科学计算的库非常不方便,还好有一些公司做好了关于python计算库在Windows安装的安装文件,这里我推荐anaconda,我推荐安装32位包,因为64位有很多库不支持.

pandas内部是使用numpy做存贮结构的,所以我们必须先了解一下numpy

先谈谈我对机器学习的理解

什么是机器学习?

我们人类有从婴儿开始就开始学习,父母教我们穿衣吃饭、老师教我们读书写字,我们开始能辨别好人坏人,开始通过自己的经验来判断新事物。

机器学习很简单,就像人一样,我们教机器通过我们教的来判断新的事物,或者在从新的事物里面学习处理新的事物。

这看起来很复杂的样子,但是从我们神经网络来看,我们可以把学习当做建立一个神经元连接,通过输入的信号得到一个输出的信号.我们只要简单的把输入的信号分类就可以了.通过无数个分类我们就可以建立复杂的神经系统,进而实现’学习’这个功能.

如何分类?

涉及到分类,假如输入的信号种类只要两种,我们就可以简单用if-else来实现分类功能,但是有时候输入信号种类个个都有细微的差别,只是遵循某种规律,这时候我们不能用简单的if-else来进行分类了,下面我就按照书的顺序来解释各种强大的分类方法.

K-近邻算法 ( k-Nearest Neighbor )

作为本书的第一个机器学习算法,K-NN算是我感觉原理最简单的一个了.

假设我们有两个点, 红点为(-1, -1)分为红类, 绿点为(1, 1)分为绿类
图片1

接下来我一个点(0, -1),这个点应该分为红还是绿呢,我们添加两条辅助线

蓝点离红点距离为1,蓝点离绿点距离为2.2,我们很轻松的可以知道这个点应该分为红类.
现在我们进一步推广,当有很多种类点的时候,当我们二维扩展到N维,给一个点a我们只要选取距离a最近的K个种类,我们就基本能判断他属于这K个种类的,这就是K-近邻的原理了.

K-近邻算法是最简单最有效的算法了,但是他也有缺点,比如他必须保存所有训练样本的数据,当训练样本很大的时候就会占用很多内存空间,我们后面会学到的KVM只取支持向量的训练样本来计算可以减少很多占用内存

而且K-近邻算法对训练数据集都要计算距离值,实际使用可能会非常耗时,我们后面学到的logistic回归能很好解决这个问题.

######### 总而言之,KNN作为小样本时非常简单粗暴,但是他无法给出任何数据的基本结构信息.接下来我们要学习用概率测量解决分类问题,这个算法能解决这个问题

(决策树)[]

###

最近学了两个python库,一个负责管理线程,一个负责管理进程,原来一直写的都
是些单线程的程序,虽然web也关于并发和多涉及到线程,但都是框架管理的,学习>过后发现了解线程和进程对python的web开发也有一定帮助。下面先谈谈这对python对线程和进程的支持再谈谈对这两个库的应用。

python对线程的支持并不是非常好,所以你可以在很多文章上批评python的多线程的弊端,但是为什么python对多线程支持不好呢,为什么其他语言比如

静态语言没有这个弊端呢。

首先我们要知道python是一种解释性语言,每段代码都需要解释器编译运行,解释器有很多种最主要的是CPython,其他还有IronPythonJython,官方的是CPython解释器,我们一般说对多线程支持不好的就是说的CPython解释器(用的人最多就省略成python解释器),python解释器为什么对多线程支持不好呢,是因为GIL的存在,当然这个存在就是因为这门语言的的特性产生的。

GIL是什么呢,下面是官方的解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

就是GIL是python的互斥锁,简单的理解就是代码会锁住python解释器。理解代码的锁定是什么必须要先了解什么是多线程

多线程表示一个主线程,多个子线程,主线程是程序执行时系统自动给你申请的一个线程,而子线程我们可以理解为一个代码块,我们可以充分利用硬件的支持比如说多核,让一个CPU执行主线程,其他CPU执行子线程,通过操作系统的虚拟内存技术让所有线程共享相同代码空间达到提高代码效率的作用,我们可以通俗的把一个进程比作一辆火车,车厢头为主线程,每节车厢为子线程,只要你车厢(子线程)越多,你运的货物也越多,但是也要考虑硬件的方面,

了解完多线程是什么我们就可以解释GIL对多核CPU工作性能的影响了,在单核CPU里面,主线程在释放GIL的时候,把CPU让给子线程,子线程代码块得到GIL,然后执行,这样就能充分利用CPU,这个GIL对单核性能的发挥没有影响,能得到100%的利用,但是在多核的的时候就有问题了,假如主线程的代码一直需要解释器来执行,
比如说下面

GIL.acquire()
try:
    while True:
        do_something()
finally:
    GIL.release()

主线程代码对GIL的锁定和解开只间隔很小的一个系统时间,子线程在其他CPU核心得到GIL解开后CPU的调度命令后才能被唤醒,但是当唤醒后,主线程的代码又锁了GIL,然后只能等待主线程下次调度命令,但是到了切换时间又切换回去到待调整状态,一直处于唤醒,等待的恶性循环,多核的功能完全没有发挥出来而且还比单核更加差,所以python因为GIL的存在对密集型的线程支持不佳,但是假如主线程是在执行想web这样等待用户输入,而不是每分每秒都在使用解释器执行代码,多线程的优势就能发挥出来。

解决方案

GIL作为解释器的一个Bug一样的存在,我们也有一定的解决方法,开线程,和用Ctype绕过解释器是我们一般的解决方法,你想了解更多可以看这个
接下来主要解绍用multiprocessing来绕过多线程的瓶颈

线程锁和进程锁

为了实现线程安全,我们也要借助锁的存在,我们先用下面的代码来验证一下多线程对于线程安全的问题。我们声明一个线程锁 threading.Lock(),
class Counter(object):
    def __init__(self, start=0):
    self.lock = threading.Lock()
    self.value = start

def increment(self):
    logging.debug('Waiting for lock')

    self.lock.acquire()
    try:
        if self.value < 8:

模拟负载

            logging.debug('Acquired lock')
            self.value = self.value + 1

    finally:
        self.lock.release()
def worker(c):
    for i in range(2):
        pause = random.random()
        logging.debug('Sleeping %0.02f', pause)
        time.sleep(pause)
        c.increment()
    logging.debug('Done')
counter = Counter()
for i in range(20):
    t = threading.Thread(target=worker,args=(counter,))
    t.start()
main_thread = threading.currentThread()
for t in threading.enumerate():
    if t is not main_thread:

保护线程

得到value值

我们运行之后得到counter.value值为8,这很好理解因为我们限制了它的大小小于8时才自增1,但是如果我们把锁去掉呢,我们把self.lock.acquire()``self.lock.release()都注释掉,得到的结果却是一个21,而且每次运行的结果都可能不一样,由于线程在实现自增的时候有一定的时间(time.sleep(2)),所以当多个进程执行的时候当他们从堆栈上取到counter.value值都为7时,这时候他们都满足
counter.value小于8,所以都执行了自增,在系统负载2秒之间(time.sleep(2))有多少个线程执行就会逃过我们给他的限制,这样就造成了线程的不安全,但是我们给他加上锁之后,无论开多少个线程,最终结果都是8。在python里面我们线程锁和进程锁我们可以看做是同一种东西。

ps:当同一线程相互争夺锁时,失败的会进出线程队列等待锁解开。

线程进程工作方式

单行

单行主要通过锁来实现,线程通过锁threading.Lock()对象创造锁,进程通过multiprocessing.Lock()对象创建进程锁,单行操作一般都是对共享数据修改的一种保护。

并行

并行操作是一般是对数据的一种共享,一般不对公共数据涉及修改,我们可以创造很多线程和进程一起并行操作,也可以限制线程和进程的并行数量,两种方式选择主要是判断代码类型是I/O密集还是线程密集型的。如何限制并行数量我们可以通过threading.Semaphore(sizenum)(进程为multiprocessing.Semaphore(sizenum))我们可以控制对共享的线程数量。进程提供了一个进程池的类型(multiprocessing.Pool),我们可以创建一个维护了一定程的进程池,但是他同时并行的数量并没有控制,只是帮我们创建了这个进程池,每个进程并不是只执行一个任务,可能执行多个方法通过一个进程.

单行混合并行

单行和并行混合我们可以通过在代码中设置锁来实现,当然python给我们提供了两种对象来实现单行和并行的控制,线程的是threading.Event()threading.Condition(),进程的是multiprocessing.Event()multiprocessing.Condition() 两种对象都是提供了一种命令指令,但是Event对象可以用来判断命令是否下达而做出相应的反应,而Condition对象更倾向于当命令下达后才执行并行的操作。

线程和进程通信方式

当我们想让线程和进程共同执行一些固定的任务,我们就需要线程和进程之间能够通信,线程和进程通信我们使用队列(Queue),进程和线程的Queue有点差异,就是进程Queue传递的对象必须pickle化,而且为了能够使用join()(保护进程)task_done(通知任务完成),我们一般使用JoinableQueue
代替Queue在进程中。

Queue对象之间通过putget通信,我们把任务put上去,Queue自动分配给当前的线程或进程,
这样就能实现对任务的流水作业话。

引用

12/26/2015 10:50:21 PM GIL维基资料

GIL博文

Pillow 是 PIL的对Python3支持的另外一个分支,当然他对Python2也兼容,由于PIL安装起来比较烦,而使用pip可以很轻松的安装Pillow,所以我选择Pillow使用,但是其核心还是PIL库的。


Python的图形处理库如PIL一直很强大,但是要想使用好它必须对图片有一定的知识储备。
使用起来很简单

from PIL import Image

引用Image包

im = Image.open('1.png')

打开图片,得到一个im对象,我们接下来就可以对这个对象进行操作(前提有这个1.png图片)

我们先看一下他的一些属性

>>> print im.format, im.size, im.mode
PNG (83, 81) RGB

第一个我们输出图片的格式,图片有很多种格式,常用的有jpg、png还有gif动图啊,PIL支持很多种格式,我们可以使用PIL轻松的将格式转换,im.save('1.jpg'),当然你可以选择格式假如你没选好后缀名的话,im.size就是图片大小,他返回的是一个元组第一个长度第二个是宽度,单位是像素。
现在就谈谈 这三个属性对应的关系吧
首先我们使用一张像素图来说吧

我们存贮图片的时候是将整个图像分成很多个相同的小方块,每个小方块我们称为像素,当然一张图片分的越小,像素越多,那么图片就越接近真实图片,上面的im.size属性就告诉我们,这张图片分成了,长为83px,宽为81px的图片,那么一共有83*81=6723个像素点,每个像素点里面存什么呢,这就是im.mode属性告诉我们的,贴一下属性有什么吧

  1. 1 (1-bit pixels, black and white, stored with one pixel per byte)
  2. L (8-bit pixels, black and white)
  3. P (8-bit pixels, mapped to any other mode using a color palette)
  4. RGB (3x8-bit pixels, true color)
  5. RGBA (4x8-bit pixels, true color with transparency mask)
  6. CMYK (4x8-bit pixels, color separation)
  7. YCbCr (3x8-bit pixels, color video format)
  8. I (32-bit signed integer pixels)
  9. F (32-bit floating point pixels)

像素存贮就是涉及到颜色的存贮,在早期的黑白游戏机,只有黑和白两种,那么每个像素点就只有1位颜色来存贮,1位只能存贮两种颜色,八位色就能存256种颜色,像八位我们能用256个油漆桶/256色调色板来形容,像上面我们使用的RGB是由三种三原色红绿蓝混合而成,我们知道大自然所有的颜色都可以用红绿蓝三种颜色调配出来,所以RGB又被称为真彩(true color),每种颜色我们都分成256种,所以我们一共有256256256=16777216种颜色可以调配,像素的其他模式我们不介绍太多,有兴趣的可以自己钻研。
那么我们知道每个像素占多少字节,又知道共有多少个像素,那我们是不是就可以直接计算出来图片大小,来验证一下

以第一张图片为例,共有8381=6723个像素点,用RGB模式,每个像素三个字节,共有67233=20667b=20kb,但是我这张图片只有11.6kb,误差太大了吧,这时候我们就要介绍一下上面那个im.format属性了,这张图片采用png格式,我们先尝试一下把他转成JPG格式吧

im.save('1.jpg')

我们再查看一下这个1.jpg的大小,只有2.24kb了,我们用PIL打开这张图片

>>> im2 = Image.open('1.jpg')
>>> print im2.format, im2.size, im2.mode 
JPEG (83, 81) RGB

图片大小没有改变,但是format变成了JPEG,而且文件大小变成原来的1/5,
JPEG和GIF和PNG是三种图片压缩技术,他们使用压缩算法把图片压缩成很小,当我们打开图片时,解密算法把他还原出来,所以我们算出来的大小与压缩后的大小是不一样的。
有了这些概念我们就能更好的使用PIL提供给我们的magic方法,下次在谈我对PIL的高级应用吧。

numpy简单来说就是python的C版数组实现,因为python原生列表虽然好使,但是生成大量数据时开销很大,而numpy是基于C的,生成大量数组非常简单,而且操作他们速度非常快.

由于numpy是基于C的,所以numpy是一种强类型的,当然numpy是可以判断数组里面是数据类型,但是我们可以显示声明他们,dtype是声明的参数,一般我们通过下面的方式简单声明一个narray

import numpy as np
arr = np.array([1, 2, 3], dtype=np.int32)

numpy还有一个强大的地方是多维数组,numpy对多维数组的支持很好.只要简单的使用嵌套序列就能被转化成多维数组.
比如
arr2 = np.array([[1, 2], [3, 4]])

numpy另一个强大的地方是矢量化,这对于科学计算来说非常有用

比如前面的arr,
我们可以简单使用

arr3 = arr * arr

得到另外一个序列 [1, 4, 9],数组间的运算应用到了元素级.

numpy之所以成为数据分析的基本数据结构,还在用取数据的灵活性

对于一维数组来说,python自身的列表就支持切片处理,numpy不仅支持切片处理,还支持列表取出,比如:

>>> num =  np.arange(10)
>>> num[[3, 1, 0]]
array([3, 1, 0])
在一维数组里面这个并没有什么优势,因为我们可以通过数据简单一个构造器[num[x] for x in [3, 1, 0]]构造出来.

当但在多维数组我们使用构造器非常繁琐了, numpy使用了很多技术使我们很方便的取出多维数组

我们先创建一个多维数组

arr = np.arange(32).reshape((8, 4))

生成的arr是

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],
   [24, 25, 26, 27],
   [28, 29, 30, 31]])

现在像一维数组一样的取出数据

In[13]: arr[[0, 3]]
Out[13]: 
array([[ 0,  1,  2,  3],
   [12, 13, 14, 15]])

我们取出了一个二维数组

试试用这个

In[14]: arr[arr > 8]
Out[14]: 
array([ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
   26, 27, 28, 29, 30, 31])

我们取出一个一维数组,这个arr > 8 是也是一个二维数组对应原来数组的每个位置都有一个布尔值代替,当numpy判断得到是一个array数组时会广播每个值来判断是否获取,这个可比你用for循环快了很多.

array([[False, False, False, False],
   [False, False, False, False],
   [False,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True]], dtype=bool)
numpy很善于处理不同的选择,当你直接给一个数组时,如上面的arr[[0, 3, 4]],它默认第二维为全部选择也就是想当与arr[[0, 3, 4], :],当你给确定的值时,他就会在第二维上取响应的值,比如
arr[[0, 3, 4], 1] 或 arr[[0, 3, 4], [1, 1, 1]]

从上面你可以看到,如果第二维你每个都想取第二个,你可以直接写一个整数就行,numpy会广播过去,假如想你分别再第一维的每个上分别对应取哪个你就可以用数组来分别选择.

有时候我们想在二维数组上面取出一个矩形块,直接使用 arr[[0, 3, 4], [1, 2, 3]]只能取出二维数组矩形块的对角线,我们这时候就可以先取出第一维的矩形列,然后再在取出的列中取出矩形行
arr[[0, 3, 4]][:, [1, 2, 3]]

当然我们还可以用二维数组来取出数据
arr[[[0], [3], [4]], [[1, 2, 3]]]

[[0], [3], [4]]代表第一维的1, 4, 5列, [[1, 2, 3]]代表第二维的2, 3, 4行.

numpy给我们提供一个函数将一维数组转换成二维数组我们可以简单使用
np.ix_([0, 3, 4], [1, 2, 3])

生成二维数组,这样我们使用

arr[np.ix_([0, 3, 4], [1, 2, 3])]

就可以取出矩形块了.