利用Python进行数据分析(原书第2版)
上QQ阅读APP看书,第一时间看更新

2.2 IPython基础

本节,我们将带你运行IPython命令行和Jupyter notebook,并介绍一些核心概念。

2.2.1 运行IPython命令行

你可以像启动标准Python解释器那样,通过ipython命令启动IPython命令行:

$ ipython
Python 3.6.0 | packaged by conda-forge | (default, Jan 132017, 23:17:12)
Type "copyright", "credits" or "license" for more information.
IPython 5.1.0-- An enhanced Interactive Python.
?           -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help       -> Python's own help system.
object?   -> Details about 'object', use 'object? ? ' for extra details.
In [1]: a = 5
In [2]: a
Out[2]: 5

你可以将Python语句输入命令行,然后按下回车键运行。当你在IPython中仅输入一个变量名,它会返回一个表示该对象的字符串:

In [5]: import numpy as np
In [6]: data = {i : np.random.randn() for i in range(7)}
In [7]: data
Out[7]:
{0: -0.20470765948471295,
  1: 0.47894333805754824,
  2: -0.5194387150567381,
  3: -0.55573030434749,
  4: 1.9657805725027142,
  5: 1.3934058329729904,
  6: 0.09290787674371767}

前两行是Python语句,其中第二行创建了一个名为data的变量,并引用了新建的Python字典。最后一行在控制台中打印了data的值。

与常见的print打印语句不同,IPython中大多数Python对象被格式化为更可读、更美观的形式。如果使用print方法在标准Python解释器中打印data变量,可读性会差一些:

>>> from numpy.random import randn
>>> data = {i : randn() for i in range(7)}
>>> print(data)
{0: -1.5948255432744511, 1: 0.10569006472787983, 2: 1.972367135977295,
3: 0.15455217573074576, 4: -0.24058577449429575, 5: -1.2904897053651216,
6: 0.3308507317325902}

IPython还提供执行任意代码块(通过复制粘贴实现)和整个Python脚本的功能。你也可以使用Jupyter notebook来处理大段的代码块,后续内容会有介绍。

2.2.2 运行Jupyter notebook

Jupyter项目中的主要组件就是notebook,这是一种交互式的文档类型,可以用于编写代码、文本(可以带标记)、数据可视化以及其他输出。Jupyter notebook与内核交互,内核是编程语言的交互式计算协议的实现。Python的Jupyter内核使用IPython系统进行内部活动。

需要启动Jupyter时,可以在终端中运行jupyter notebook命令:

$ jupyter notebook
[I 15:20:52.739 NotebookApp] Serving notebooks from local directory:
/home/wesm/code/pydata-book
[I 15:20:52.739 NotebookApp] 0 active kernels
[I 15:20:52.739 NotebookApp] The Jupyter Notebook is running at:
http://localhost:8888/
[I 15:20:52.740 NotebookApp] Use Control-C to stop this server and shut down
all kernels (twice to skip confirmation).
Created new window in existing browser session.

在很多平台上,Jupyter会自动打开你的默认网络浏览器(除非你使用了--no-browser命令)。你可以通过http地址来浏览notebook,地址是http://localhost:8888/。图2-1是谷歌Chrome浏览器中的效果。

很多人使用Jupyter作为本地计算环境,但它其实也可以部署在服务器端,远程访问。我不会介绍这些内容,但如果有需要的话,建议你在网上搜索相关的主题。

图2-1:Jupyter notebook登录页面

点击新建按钮选择“Python 3”或者“conda[default]”即可新建一个笔记本,然后你应该就可以看到如图2-2所示的内容。如果这是你第一次使用,请尝试点击空的代码“单元”,输入一行Python代码,然后按下Shift-Enter来执行。

图2-2:Jupyter新建笔记本视图

当你保存笔记本的时候(在File菜单下有“Save and Checkpoint”选项),会自动生成一个后缀名为.ipynb的文件。这种文件格式会包含笔记本中当前的所有内容(包括已经产生的代码输出)。这些内容可以被其他的Jupyter用户载入、编辑。要载入一个已经存在的笔记本,可以将文件放置在你启动命令行进程的路径下(或者是该路径下的子文件夹中),然后在登录页面双击文件名。你可以试试我在GitHub上的笔记本仓库wesm/pydata-book。见图2-3。

图2-3:通过Jupyter打开已经存在的笔记本

尽管Jupyter notebook提供了与IPython命令行不同的体验,但几乎所有的命令和工具都可以在两种环境下使用。

2.2.3 Tab补全

从表面上看,IPython只是看起来与标准Python解释器(通过python命令启动)有一些装饰性的区别。相较于标准Python命令行,IPython的提升之一就是tab补全功能,通常该功能在IDE或者其他交互式计算分析环境中才有。当在命令行输入表达式时,按下Tab键即可为任意变量(对象、函数等)搜索命名空间,与你目前已输入的字符进行匹配:

In [1]: an_apple = 27
In [2]: an_example = 42
In [3]: an<Tab>
an_apple     and           an_example  any

在上述示例中,请注意IPython同时列出了我已定义的两个变量、关键字and和内建函数any。当然,你还可以在输入英文的句号之后,按下tab,对方法、属性的名称进行补全:

In [3]: b = [1, 2, 3]
In [4]: b.<Tab>
b.append  b.count    b.insert  b.reverse
b.clear    b.extend  b.pop      b.sort
b.copy     b.index    b.remove

模块也可以通过相同的方式补全:

In [1]: import datetime
In [2]: datetime.<Tab>
datetime.date            datetime.MAXYEAR        datetime.timedelta
datetime.datetime       datetime.MINYEAR        datetime.timezone
datetime.datetime_CAPI datetime.time            datetime.tzinfo

在Jupyter notebook和新版的IPython(5.0及以上)中,自动补全是在下拉选项中展现,而不是文本输出。

请注意IPython默认情况下隐藏了以下划线开始的方法和属性,诸如魔术方法、内部“私有”方法和属性,以避免杂乱的显示(使新手混淆)。这些你当然也是可以使用tab补全的,但是必须先输入下划线才能看到它们。如果你总是想在tab补全时直接看到它们,则需要修改IPython配置。参见IPython官方文档可以找到相关内容。

tab补全除了在搜索交互命名空间和补全对象或模块属性时有用,在很多其他上下文场景中也有用。当输入任意路径(甚至是Python字符串)时,按下Tab键将补全你的计算机文件系统中匹配你输入内容的值:

In [7]: datasets/movielens/<Tab>
datasets/movielens/movies.dat     datasets/movielens/README
datasets/movielens/ratings.dat    datasets/movielens/users.dat
In [7]: path = 'datasets/movielens/<Tab>
datasets/movielens/movies.dat     datasets/movielens/README
datasets/movielens/ratings.dat    datasets/movielens/users.dat

与%run命令搭配使用(参见2.2.5节),该功能将为你节省大量键盘输入。

tab补全的另一个应用场景是在函数的关键字参数(包含=号)中节约时间,见图2-4。

图2-4:在Jupyter notebook中自动补全函数关键字参数

本节之后将更进一步介绍函数。

2.2.4 内省

在一个变量名的前后使用问号(?)可以显示一些关于该对象的概要信息:

In [8]: b = [1, 2, 3]
In [9]: b?
Type:        list
String Form:[1, 2, 3]
Length:      3
Docstring:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
In [10]: print?
Docstring:
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file:  a file-like object (stream); defaults to the current sys.stdout.
sep:    string inserted between values, default a space.
end:    string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
Type:       builtin_function_or_method

这就是对象内省。如果对象是一个函数或实例方法且文档字符串已经写好,则文档字符串会显示出来。假设已经写好如下函数(你可以在IPython或Jupyter中复现):

def add_numbers(a, b):
    """
    Add two numbers together
Returns
-------
the_sum : type of arguments
"""
return a + b

然后使用?来显示文档字符串:

In [11]: add_numbers?
Signature: add_numbers(a, b)
Docstring:
Add two numbers together
Returns
-------
the_sum : type of arguments
File:       <ipython-input-9-6a548a216e27>
Type:       function

使用双问号??可以显示函数的源代码:

In [12]: add_numbers? ?
Signature: add_numbers(a, b)
Source:
def add_numbers(a, b):
    """
    Add two numbers together
    Returns
    -------
    the_sum : type of arguments
    """
    return a + b
File:       <ipython-input-9-6a548a216e27>
Type:       function

?有一个终极用途,可以像标准Unix或Windows命令行一样搜索IPython命名空间。把一些字符和通配符(星号*)结合在一起,会显示所有匹配通配符表达式的命名。例如,我们可以得到NumPy顶层函数中包含load的函数名列表:

In [13]: np.*load*?
np.__loader__
np.load
np.loads
np.loadtxt
np.pkgload

2.2.5 %run命令

可以在IPython会话中使用%run命令运行任意的Python程序文件。假设你已经在ipython_script_test.py中写好了如下的简单脚本:

def f(x, y, z):
    return (x + y) / z
a = 5
b = 6
c = 7.5
result = f(a, b, c)

可以将文件名作为参数传给%run命令:

In [14]: %run ipython_script_test.py

脚本是在空白命名空间(没有导入模块或其他定义变量)中运行的,因此这个行为与在命令行中使用python script.py来运行程序是相同的。文件中定义的所有变量(导入的、函数中的、全局定义的)在运行后,可以在IPython命令行中使用(除非出现某种异常):

In [15]: c
Out [15]: 7.5
In [16]: result
Out[16]: 1.4666666666666666

如果一个Python脚本需要命令行提供参数(通过sys.argv获得),那么则需要在命令行的文件路径后面加上参数进行传递:

如果你想让待运行的脚本使用交互式IPython命名空间中已有的变量,请使用%run -i替代普通的%run命令。

在Jupyter notebook中,如果你想将脚本导入一个代码单元,可以使用%load魔术函数:

>>> %load ipython_script_test.py
    def f(x, y, z):
        return (x + y) / z
    a = 5
    b = 6
    c = 7.5
    result = f(a, b, c)

2.2.5.1 中断运行中的代码

在任意代码运行时按下Ctrl-C,无论脚本是通过%run或是其他长命令运行的,都将引起KeyboardInterrupt。除了某些特殊情况,这将导致所有的Python程序立即停止运行。

当一段Python代码被其他已经编译的扩展模块调用时,按下Ctrl-C并不会让程序立即停止运行。在这些情况下,你需要等到控制流重新返回Python解释器,在更糟糕的情况下可能要强制结束Python进程。

2.2.6 执行剪贴板中的程序

如果你正在使用Jupyter notebook,你可以将代码复制粘贴到代码单元,然后运行。在IPython中可以直接运行剪贴板中的程序。假设你在其他的应用中写了如下代码:

x = 5
y = 7
if x > 5:
    x += 1
    y = 8

使用以上代码,最简单的方法就是%paste和%cpaste魔术函数。%paste会获得剪贴板中的所有文本,并在命令行中作为一个代码块去执行:

In [17]: %paste
x = 5
y = 7
if x > 5:
    x += 1
    y = 8

## -- 传入文本的结尾 --

%cpaste与之类似,只不过它会给出一个特殊的提示符,让你粘贴代码:

In [18]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:x = 5
:y = 7
:if x > 5:
:     x += 1
:     y = 8
:--

:

使用%cpaste,你可以自由地在执行代码前尽可能多地粘贴代码。你也许会想着使用%cpaste在执行前看下粘贴的代码,如果你发现粘贴的代码有误,可以按下Ctrl-C来中断%cpaste提示符。

2.2.7 终端快捷键

IPython有很多用于浏览提示、查看历史命令的快捷键(Emacs文本编辑器和Unix bash命令行的使用者会很熟悉)。表2-1总结了最常用的快捷键。图2-5展示了部分快捷功能,比如光标移动。

图2-5:IPython命令行的部分快捷键

表2-1:标准IPython快捷键

需要注意的是,Jupyter notebook有一个独立的快捷键集合用于导航和编辑。由于Jupyter notebook的快捷键更新比IPython更为频繁,建议你使用Jupyter notebook集成在菜单栏中的帮助系统。

2.2.8 关于魔术命令

IPython的特殊命令(没有内建到Python自身中去)被称为“魔术”命令。这些命令被设计用于简化常见任务,确保用户更容易控制IPython系统的行为。魔术命令的前缀符号是百分号%。例如,你可以使用%timeit来检查一段Python语句的执行时间,比如一个矩阵操作(后续将会详细讨论%timeit):

In [20]: a = np.random.randn(100, 100)
In [20]: %timeit np.dot(a, a)
10000 loops, best of 3: 20.9 µs per loop

魔术命令可以看作是IPython系统内部的命令行程序。大多数魔术命令都可以使用?查看额外的“命令行”选项:

In [21]: %debug?
Docstring:
::
  %debug [--breakpoint FILE:LINE] [statement [statement ...]]
Activate the interactive debugger.
This magic command support two ways of activating debugger.
One is to activate debugger before executing code.  This way, you
can set a break point, to step through the code from the point.
You can use this mode by giving statements to execute and optionally
a breakpoint.
The other one is to activate debugger in post-mortem mode.  You can
activate this mode simply running %debug without any argument.
If an exception has just occurred, this lets you inspect its stack
frames interactively.  Note that this will always work only on the last
traceback that occurred, so you must call this quickly after an
exception that you wish to inspect has fired, because if another one
occurs, it clobbers the previous one.
If you want IPython to automatically do this on every exception, see
the %pdb magic for more details.
positional arguments:
  statement        Code to run in debugger. You can omit this in cell
                          magic mode.
optional arguments:
  --breakpoint <FILE:LINE>, -b <FILE:LINE>
                          Set break point at LINE in FILE.

魔术函数也可以不加百分号%就使用,只要没有变量被定义为与魔术函数相同的名字即可。这种特性被称为自动魔术,通过%automagic进行启用/禁用关。

一些魔术函数也像Python函数一样,其输出可以赋给一个变量:

In [22]: %pwd
Out[22]: '/home/wesm/code/pydata-book
In [23]: foo = %pwd
In [24]: foo
Out[24]: '/home/wesm/code/pydata-book'

由于IPython的文档可以在系统内访问,建议使用%quickref或者%magic探索所有的特殊命令。表2-2是在IPython中高效进行交互式计算和Python开发所常用的重要命令。

表2-2:IPython常用魔术命令

2.2.9 matplotlib集成

IPython能在分析计算领域流行的原因之一,就是它和数据可视化、用户界面库(如matplotlib)的良好集成。即使你从未使用过matplotlib也不必担心,本书的后续章节会详细讨论该部分内容。%matplotlib魔术函数可以设置matplotlib与IPython命令行或Jupyter notebook的集成。这个命令很重要,否则你创建的图可能不会出现(notebook),或者直到结束也无法控制会话(命令行)。

在IPython命令行中,运行%matplotlib命令可以生成多个绘图窗口,而无须干扰控制台的会话。

In [26]: %matplotlib
Using matplotlib backend: Qt4Agg

在Jupyter中,命令会有些许不同(见图2-6):

In [26]: %matplotlib inline

图2-6:Jupyter内联matplotlib绘图