1. 程式人生 > >《利用Python進行資料分析》第二版,第二章精選筆記

《利用Python進行資料分析》第二版,第二章精選筆記

因為這本書是專注於Python資料處理的,對於一些Python的資料結構和庫的特性難免不足。因此,本章和第3章的內容只夠你能學習本書後面的內容。

在我來看,沒有必要為了資料分析而去精通Python。我鼓勵你使用IPython shell和Jupyter試驗示例程式碼,並學習不同型別、函式和方法的文件。雖然我已盡力讓本書內容循序漸進,但讀者偶爾仍會碰到沒有之前介紹過的內容。

本書中使用的工具最好在IPython和Jupyter中親自嘗試。當你學會了如何啟用Ipython和Jupyter,我建議你跟隨示例程式碼進行練習。與任何鍵盤驅動的操作環境一樣,記住常見的命令也是學習曲線的一部分。

筆記:本章沒有介紹Python的某些概念,如類和麵向物件程式設計,你可能會發現它們在Python資料分析中很有用。
為了加強Python知識,我建議你學習官方Python教程,

https://docs.python.org/3/,或是通用的Python教程書籍,比如:

  • Python Cookbook,第3版,David Beazley和Brian K. Jones著(O’Reilly)
  • 流暢的Python,Luciano Ramalho著 (O’Reilly)
  • 高效的Python,Brett Slatkin著 (Pearson)

Tab補全

從外觀上,IPython shell和標準的Python直譯器只是看起來不同。IPython shell的進步之一是其它IDE和互動計算分析環境都有的tab補全功能。在shell中輸入表示式,按下Tab,會搜尋已輸入變數(物件、函式等等)的名稱空間:

筆記:注意,預設情況下,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

自省

在變數前後使用問號?,可以顯示物件的資訊:

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

這可以作為物件的自省。如果物件是一個函式或例項方法,定義過的文件字串,也會顯示出資訊。假設我們寫了一個如下的函式:

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的名稱空間。字元與萬用字元結合可以匹配所有的名字。例如,我們可以獲得所有包含load的頂級NumPy名稱空間:

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

%run命令

你可以用%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)

可以如下執行:

In [14]: %run ipython_script_test.py

如果一個Python指令碼需要命令列引數(在sys.argv中查詢),可以在檔案路徑之後傳遞,就像在命令列上執行一樣。

筆記:如果想讓一個指令碼訪問IPython已經定義過的變數,可以使用%run -i

在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)

中斷執行的程式碼

程式碼執行時按Ctrl-C,無論是%run或長時間執行命令,都會導致KeyboardInterrupt。這會導致幾乎左右Python程式立即停止,除非一些特殊情況。

警告:當Python程式碼呼叫了一些編譯的擴充套件模組,按Ctrl-C不一定將執行的程式立即停止。在這種情況下,你必須等待,直到控制返回Python直譯器,或者在更糟糕的情況下強制終止Python程序。

鍵盤快捷鍵

IPython有許多鍵盤快捷鍵進行導航提示(類似Emacs文字編輯器或UNIX bash Shell)和互動shell的歷史命令。表2-1總結了常見的快捷鍵。圖2-5展示了一部分,如移動游標。

img

圖2-5 IPython shell中一些快捷鍵的說明

img

Jupyter notebooks有另外一套龐大的快捷鍵。因為它的快捷鍵比IPython的變化快,建議你參閱Jupyter notebook的幫助文件。

魔術命令

IPython中特殊的命令(Python中沒有)被稱作“魔術”命令。這些命令可以使普通任務更便捷,更容易控制IPython系統。魔術命令是在指令前新增百分號%字首。例如,可以用%timeit(這個命令後面會詳談)測量任何Python語句,例如矩陣乘法,的執行時間:

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

魔術函式預設可以不用百分號,只要沒有變數和函式名相同。這個特點被稱為“自動魔術”,可以用%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的文件可以在shell中開啟,我建議你用%quickref%magic學習下所有特殊命令。表2-2列出了一些可以提高生產率的互動計算和Python開發的IPython指令。

img

表2-2 一些常用的IPython魔術命令

整合Matplotlib

IPython在分析計算領域能夠流行的原因之一是它非常好的集成了資料視覺化和其它使用者介面庫,比如matplotlib。不用擔心以前沒用過matplotlib,本書後面會詳細介紹。%matplotlib魔術函式配置了IPython shell和Jupyter notebook中的matplotlib。這點很重要,其它建立的圖不會出現(notebook)或獲取session的控制,直到結束(shell)。

在IPython shell中,執行%matplotlib可以進行設定,可以建立多個繪圖視窗,而不會干擾控制檯session:

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

在JUpyter中,命令有所不同(圖2-6):

In [26]: %matplotlib inline

img

圖2-6 Jupyter行內matplotlib作圖

2.3 Python語法基礎

在本節中,我將概述基本的Python概念和語言機制。在下一章,我將詳細介紹Python的資料結構、函式和其它內建工具。

語言的語義

Python的語言設計強調的是可讀性、簡潔和清晰。有些人稱Python為“可執行的虛擬碼”

理解Python的引用的含義,資料是何時、如何、為何複製的,是非常重要的。尤其是當你用Python處理大的資料集時。

筆記:賦值也被稱作繫結,我們是把一個名字繫結給一個物件。變數名有時可能被稱為繫結變數。

當你將物件作為引數傳遞給函式時,新的局域變數建立了對原始物件的引用,而不是複製。如果在函式裡繫結一個新物件到一個變數,這個變動不會反映到上一層。

知道物件的型別很重要,最好能讓函式可以處理多種型別的輸入。你可以用isinstance函式檢查物件是某個型別的例項:

In [21]: a = 5

In [22]: isinstance(a, int)
Out[22]: True

isinstance可以用型別元組,檢查物件的型別是否在元組中:

In [23]: a = 5; b = 4.5

In [24]: isinstance(a, (int, float))
Out[24]: True

In [25]: isinstance(b, (int, float))
Out[25]: True

子型別

經常地,你可能不關心物件的型別,只關心物件是否有某些方法或用途。這通常被稱為“鴨子型別”,來自“走起來像鴨子、叫起來像鴨子,那麼它就是鴨子”的說法。例如,你可以通過驗證一個物件是否遵循迭代協議,判斷它是可迭代的。對於許多物件,這意味著它有一個__iter__魔術方法,其它更好的判斷方法是使用iter函式:

def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: # not iterable
        return False

這個函式會返回字串以及大多數Python集合型別為True

In [29]: isiterable('a string')
Out[29]: True

In [30]: isiterable([1, 2, 3])
Out[30]: True

In [31]: isiterable(5)
Out[31]: False

我總是用這個功能編寫可以接受多種輸入型別的函式。常見的例子是編寫一個函式可以接受任意型別的序列(list、tuple、ndarray)或是迭代器。你可先檢驗物件是否是列表(或是NUmPy陣列),如果不是的話,將其轉變成列表:

if not isinstance(x, list) and isiterable(x):
    x = list(x)

引入

在Python中,模組就是一個有.py副檔名、包含Python程式碼的檔案。假設有以下模組:

# some_module.py
PI = 3.14159

def f(x):
    return x + 2

def g(a, b):
    return a + b

如果想從同目錄下的另一個檔案訪問some_module.py中定義的變數和函式,可以:

import some_module
result = some_module.f(5)
pi = some_module.PI

或者:

from some_module import f, g, PI
result = g(5, PI)

使用as關鍵詞,你可以給引入起不同的變數名:

import some_module as sm
from some_module import PI as pi, g as gf

r1 = sm.f(pi)
r2 = gf(6, pi)

二元運算子和比較運算子

img

表2-3 二元運算子

img

位元組和Unicode

在Python 3及以上版本中,Unicode是一級的字串型別,這樣可以更一致的處理ASCII和Non-ASCII文字。在老的Python版本中,字串都是位元組,不使用Unicode編碼。假如知道字元編碼,可以將其轉化為Unicode。看一個例子:

In [76]: val = "español"

In [77]: val
Out[77]: 'español'

可以用encode將這個Unicode字串編碼為UTF-8:

In [78]: val_utf8 = val.encode('utf-8')

In [79]: val_utf8
Out[79]: b'espa\xc3\xb1ol'

In [80]: type(val_utf8)
Out[80]: bytes

如果你知道一個位元組物件的Unicode編碼,用decode方法可以解碼:

In [81]: val_utf8.decode('utf-8')
Out[81]: 'español'

型別轉換

str、bool、int和float也是函式,可以用來轉換型別:

In [91]: s = '3.14159'

In [92]: fval = float(s)

In [93]: type(fval)
Out[93]: float

In [94]: int(fval)
Out[94]: 3

In [95]: bool(fval)
Out[95]: True

In [96]: bool(0)
Out[96]: False

日期和時間

Python內建的datetime模組提供了datetimedatetime型別。datetime型別結合了datetime,是最常使用的:

In [102]: from datetime import datetime, date, time

In [103]: dt = datetime(2011, 10, 29, 20, 30, 21)

In [104]: dt.day
Out[104]: 29

In [105]: dt.minute
Out[105]: 30

根據datetime例項,你可以用datetime提取出各自的物件:

In [106]: dt.date()
Out[106]: datetime.date(2011, 10, 29)

In [107]: dt.time()
Out[107]: datetime.time(20, 30, 21)

strftime方法可以將datetime格式化為字串:

In [108]: dt.strftime('%m/%d/%Y %H:%M')
Out[108]: '10/29/2011 20:30'

strptime可以將字串轉換成datetime物件:

In [109]: datetime.strptime('20091031', '%Y%m%d')
Out[109]: datetime.datetime(2009, 10, 31, 0, 0)

表2-5列出了所有的格式化命令。

img

表2-5 Datetime格式化指令(與ISO C89相容)

當你聚類或對時間序列進行分組,替換datetimes的time欄位有時會很有用。例如,用0替換分和秒:

In [110]: dt.replace(minute=0, second=0)
Out[110]: datetime.datetime(2011, 10, 29, 20, 0)

因為datetime.datetime是不可變型別,上面的方法會產生新的物件。

兩個datetime物件的差會產生一個datetime.timedelta型別:

In [111]: dt2 = datetime(2011, 11, 15, 22, 30)

In [112]: delta = dt2 - dt

In [113]: delta
Out[113]: datetime.timedelta(17, 7179)

In [114]: type(delta)
Out[114]: datetime.timedelta

結果timedelta(17, 7179)指明瞭timedelta將17天、7179秒的編碼方式。

timedelta新增到datetime,會產生一個新的偏移datetime

In [115]: dt
Out[115]: datetime.datetime(2011, 10, 29, 20, 30, 21)

In [116]: dt + delta
Out[116]: datetime.datetime(2011, 11, 15, 22, 30)

三元表示式

Python中的三元表示式可以將if-else語句放到一行裡。語法如下:

value = true-expr if condition else false-expr

true-exprfalse-expr可以是任何Python程式碼。它和下面的程式碼效果相同:

if condition:
    value = true-expr
else:
    value = false-expr

下面是一個更具體的例子:

In [126]: x = 5

In [127]: 'Non-negative' if x >= 0 else 'Negative'
Out[127]: 'Non-negative'

和if-else一樣,只有一個表示式會被執行。因此,三元表示式中的if和else可以包含大量的計算,但只有True的分支會被執行。

雖然使用三元表示式可以壓縮程式碼,但會降低程式碼可讀性。