Python 提供了多種內建資料集合,其中 List、Tuple 和字典是最常用的型別。List 作為可變序列,允許修改元素,適用於需要動態調整資料的場景。Tuple 則為不可變序列,適用於固定結構的資料,例如座標或 HTTP Headers。字典則提供了鍵值對映的功能,方便快速查詢和管理資料。理解這些資料集合的特性和操作技巧,對於提升 Python 資料處理效率至關重要。常見操作包含使用索引和切片擷取特定資料區段、使用 in 運算子檢查元素是否存在,以及使用 lambda 函式和生成器表示式簡化程式碼並提升效能。這些技巧在處理實際資料,例如分析年份和乳酪消費量的關係時,能發揮重要作用,有效地提取和分析資訊。
資料蒐集與整理
在前面的章節中,我們已經介紹了一些資料集合。現在是時候深入瞭解這些集合是什麼,以及如何有效地使用它們。正如我們在第一章《我們的間諜工具包》中所觀察到的,Python 提供了多種不同型別的數字。常用的數字是內建的,而更專業的數字則需要從標準函式庫中匯入。
同樣地,Python 有許多內建的集合型別。此外,標準函式庫中還提供了大量的額外集合型別。我們將重點介紹內建的列表(list)、元組(tuple)、字典(dictionary)和集合(set)。這些涵蓋了處理資料群組的基本需求。
使用 Python 列表
Python 的列表類別可以被概括為一個可變序列。可變性意味著我們可以新增、更改和移除專案(列表可以被更改)。序列意味著專案是根據它們在列表中的位置來存取的。
語法非常簡單;我們把資料專案放在 [] 中,並用 , 分隔專案。我們可以在序列中使用任何 Python 物件。
總公司需要有關選定乳酪品種的人均消費量的資訊。雖然總公司並未向現場特工透露太多資訊,但我們知道他們經常想了解自然資源和戰略經濟優勢。
我們可以在 http://www.ers.usda.gov/datafiles/Dairy_Data/chezcon_1_.xls 找到乳酪消費資料。遺憾的是,資料採用專有的試算表格式,難以處理。要自動化資料收集,我們需要像 Project Stingray 這樣的工具來從檔案中提取資料。對於手動資料收集,我們可以複製和貼上資料。
以下是從 2000 年開始到 2010 年的資料;我們將用它來展示一些簡單的列表處理:
>>> cheese = [29.87, 30.12, 30.60, 30.66, 31.33, 32.62, 32.73, 33.50, 32.84, 33.02]
>>> len(cheese)
10
>>> min(cheese)
29.87
>>> cheese.index(max(cheese))
7
內容解密:
cheese = [29.87, 30.12, 30.60, 30.66, 31.33, 32.62, 32.73, 33.50, 32.84, 33.02]:建立一個名為cheese的列表物件,並將其指定給變數cheese。len(cheese):使用len()函式取得列表中的專案數量,結果為 10。min(cheese):使用min()函式找出列表中的最小值,結果為 29.87。cheese.index(max(cheese)):先使用max()函式找出列表中的最大值,然後使用index()方法找出該最大值在列表中的索引,結果為 7。
我們建立了一個列表物件並將其指定給 cheese 變數。使用 min() 函式可以找出序列中的最小值。index() 方法則是在序列中搜尋匹配的值。我們發現,使用 max() 函式找到的最大消費量的索引是 7,對應到 2007 年。之後,乳酪的消費量略有下降。
注意,我們使用了字首函式表示法(min()、max()、len() 等)和方法函式表示法(cheese.index() 等)。Python 提供了豐富多樣的表示法,並不拘泥於只使用方法函式。
由於列表是可變的,我們可以向列表中追加額外的值。我們可以使用 cheese.extend() 方法來擴充套件給定的列表:
>>> cheese.extend([32.92, 33.27, 33.51])
>>> cheese
[29.87, 30.12, 30.6, 30.66, 31.33, 32.62, 32.73, 33.5, 32.84, 33.02, 32.92, 33.27, 33.51]
內容解密:
cheese.extend([32.92, 33.27, 33.51]):使用extend()方法將新的列表[32.92, 33.27, 33.51]新增到原有的cheese列表中。- 輸出結果為
[29.87, 30.12, 30.6, 30.66, 31.33, 32.62, 32.73, 33.5, 32.84, 33.02, 32.92, 33.27, 33.51],顯示了更新後的cheese列表。
我們也可以使用 +運算元來合併兩個列表。
使用列表索引操作
我們可以使用 cheese[index] 表示法來存取個別專案:
>>> cheese[0]
29.87
>>> cheese[1]
30.12
內容解密:
cheese[0]:存取cheese列表中的第一個專案,結果為29.87。cheese[1]:存取cheese列表中的第二個專案,結果為30.12。
這允許我們從列表中挑選特定的專案。由於列表已經排序,第一個專案是最小值,第二個專案是次小值。我們也可以從列表末尾開始倒數索引:
>>> cheese[-2]
33.5
>>> cheese[-1]
33.51
內容解密:
cheese[-2]:存取cheese列表中倒數第二個專案,結果為33.5。cheese[-1]:存取cheese列表示中的最後一個專案,結果為33.51。
在排序後的資料中,-2 對應到次大值,而 -1 對應到最大值。在原始的未排序資料中,cheese[-2] 將對應到2009年的資料。
切片操作
我們也可以從列表中擷取切片。一些常見的切片操作如下所示:
>>> cheese[:5]
[29.87, 30.12, 30.6, 30.66, 31.33]
>>> cheese[5:]
[32.62, 32.73, 32.84, 32.92, 33.02, 33.27, 33.5, 33.51]
內容解密:
1. cheese[:5]:擷取前五個元素,即乳酪消費量最小的五個值。
2. cheese[5:]:擷取從第六個元素開始到結尾的所有元素,即乳酪消費量較大的值。
使用in運算子進行比較
在處理集合時,我們發現有一個新的比較運算子——in。我們可以使用簡單的in測試來檢視一個值是否出現在集合中的任何地方:
>>>30 .5 in cheese
False
>>>33 .5 in cheese
True
內容解密:
1. in運算子檢查某個值是否存在於集合中,例如檢查特定數字是否在乳酪資料中。
2. True或False代表檢查結果,表示該值是否存在於序列中。
使用Python Tuple的實務應用
Python的tuple類別可簡要地描述為不可變序列(immutable sequence)。不可變意味著一旦建立,tuple就不能被改變。數值3是不可變的,它始終是3。序列意味著其中的專案是根據它們在tuple中的位置來存取的。
Tuple的基本語法與特性
Tuple的語法非常簡單;我們需要將資料專案放在()中,並用,分隔這些專案。我們可以在序列中使用任何Python物件。其理念是建立一個看起來像數學座標的物件,例如(3, 4)。
Tuple在Python內部被廣泛使用。當我們使用多重指定時,例如以下程式碼的右側建立了一個tuple,而左側則將其分解:
power, value = 0, 1
右側建立了一個二元tuple (0, 1)。語法不需要在tuple周圍加上()。左側則分解了一個二元tuple,將值賦給兩個不同的變數。
Tuple的實務應用場景
我們通常使用tuple來表示那些元素數量由問題域(problem domain)固定的資料物件。我們經常使用tuple來表示座標對,例如緯度和經度。由於tuple的大小不能改變,因此我們不需要像list那樣具有靈活的長度。對於一個應該只有兩個值(緯度和經度)的tuple來說,三元tuple代表什麼意義?不同的問題可能涉及經度、緯度和海拔;在這種情況下,我們使用的是三元tuple。在這些例子中使用二元tuple或三元tuple是問題的基本特徵:我們不需要改變物件來新增或刪除值。
HTTP Headers中的Tuple應用
當我們檢視requests和responses中的HTTP headers時,我們看到它們被表示為一個二元tuple的list,例如('Content-Type', 'text/html; charset=utf-8')。每個tuple都有一個header名稱(‘Content-Type’)和header值(’text/html; charset=utf-8’)。
年份與乳酪消費量的Tuple應用
這裡有一個使用二元tuple來包含年份和乳酪消費量的例子:
year_cheese = [(2000, 29.87), (2001, 30.12), (2002, 30.6), (2003, 30.66),
(2004, 31.33), (2005, 32.62), (2006, 32.73), (2007, 33.5),
(2008, 32.84), (2009, 33.02), (2010, 32.92), (2011, 33.27),
(2012, 33.51)]
對Tuple List進行分析
這種list-of-tuple結構允許我們對資料進行稍微簡單的分析。以下是兩個例子:
>>> max(year_cheese, key=lambda x: x[1])
(2012, 33.51)
>>> min(year_cheese, key=lambda x: x[1])
(2000, 29.87)
我們對我們的tuple list應用了max()函式。max()函式的第二個引數是另一個函式——在這個例子中,是一個匿名lambda物件——它評估每個tuple中的第二個值。
使用Lambda函式與命名函式
以下是兩個更多的例子,展示了lambda物件的行為:
>>> (2007, 33.5)[1]
33.5
>>> (lambda x: x[1])((2007, 33.5))
33.5
(2007, 33.5)這個二元tuple具有[1] get item操作;這將選取位置1上的專案,即33.5。位置0上的專案是年份2007。
(lambda x: x[1])表示式建立了一個匿名lambda函式。然後,我們可以將這個函式應用於(2007, 33.5)這個二元tuple。由於x[1]表示式選取索引位置1上的專案,因此我們得到33.5這個值。
#### 內容解密:
max()和min()函式用於找出list中元素的最大值和最小值。key引數指定了一個函式,用於在比較元素之前對它們進行轉換。- 在這個例子中,
lambda x: x[1]是一個匿名函式,它傳回每個tuple的第二個元素。 - 這使得
max()和min()能夠根據tuple的第二個元素(乳酪消費量)來找出最大值和最小值。
使用命名函式替代Lambda
我們可以建立一個完全定義的、命名的函式,而不是使用lambda,如下所示:
def by_weight(yr_wt_tuple):
year, weight = yr_wt_tuple
return weight
命名函式有兩個優點:它有名稱,並且可以有多行程式碼。Lambda函式的優點是當整個函式可以簡化為單一表達式時,它非常簡潔。
#### 內容解密:
by_weight函式接受一個二元tuple作為輸入,並傳回第二個元素(乳酪消費量)。- 這使得我們能夠根據乳酪消費量對tuple list進行排序。
sorted()函式使用by_weight函式作為key引數,對tuple list進行排序。
對Tuple List進行排序
我們可以使用這種技術對這些二元tuple進行排序,使用函式而不是lambda,如下所示:
>>> by_cheese = sorted(year_cheese, key=by_weight)
>>> by_cheese
[(2000, 29.87), (2001, 30.12), (2002, 30.6), (2003, 30.66), (2004, 31.33),
(2005, 32.62), (2006, 32.73), (2008, 32.84), (2010, 32.92), (2009, 33.02),
(2011, 33.27), (2007, 33.5), (2012, 33.51)]
#### 內容解密:
sorted()函式傳回一個新的list,包含原始list中的元素,但已排序。key引數指定了一個函式,用於在比較元素之前對它們進行轉換。- 在這個例子中,
by_weight函式用於根據乳酪消費量對tuple list進行排序。
使用生成器表示式處理Tuple List
如果我們想要根據給定的年份找到乳酪產量,我們需要在這個二元tuple序列中搜尋匹配的年份。我們不能簡單地使用list.index()函式來定位專案,因為我們只使用了專案的一部分。一種策略是使用生成器表示式從list中提取年份,如下所示:
>>> years = [item[0] for item in year_cheese]
>>> years
[2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012]
#### 內容解密:
- 生成器表示式用於從
year_cheeselist中提取每個tuple的第一個元素(年份)。 - 結果被收集到一個新的list中,並賦給
years變數。 - 這使得我們能夠根據年份來搜尋對應的乳酪消費量。
使用Python字典對映取得智慧資料
在處理來自RESTful API的資料時,Python字典是一種非常有用的資料結構。字典包含從鍵到值的對映,可以被視為一個可變的對映結構。這意味著我們可以新增、更改和刪除其中的專案。
字典的基本語法和特性
字典的語法非常簡單,我們使用大括號 {} 將鍵值對包裹起來,鍵和值之間使用冒號 : 分隔,不同的鍵值對之間使用逗號 , 分隔。值可以是任何型別的Python物件,而鍵則必須是不可變的物件,如字串、數字或元組。
示例:建立和存取字典
spot_rate = {'currency': 'EUR', 'amount': '361.56'}
print(spot_rate['currency']) # 輸出:EUR
print(spot_rate['amount']) # 輸出:361.56
import decimal
print(decimal.Decimal(spot_rate['amount'])) # 輸出:Decimal('361.56')
在這個例子中,我們建立了一個名為 spot_rate 的字典,並存取了其中的鍵 currency 和 amount 對應的值。
修改字典中的值
由於字典是可變的,我們可以輕易地更改與鍵相關聯的值。
form = {"currency": "EUR"}
form['currency'] = "USD"
print(form) # 輸出:{'currency': 'USD'}
內容解密:
- 建立一個包含單一鍵值對的字典
form。 - 將
form中鍵currency的值從"EUR"修改為"USD"。 - 列印修改後的
form字典。
安全地取得值
當嘗試取得一個可能不存在的鍵的值時,可以使用 dict.get(key, default) 方法來避免 KeyError 異常。
print(spot_rate.get('amount')) # 輸出:'361.56'
print(spot_rate.get('oops')) # 輸出:None
print(spot_rate.get('oops', '#Missing')) # 輸出:'#Missing'
內容解密:
- 使用
get()方法取得鍵amount的值,如果存在則傳回其值。 - 嘗試取得不存在的鍵
oops的值,由於未提供預設值,因此傳回None。 - 再次嘗試取得鍵
oops的值,這次提供了預設值'#Missing',因此傳回該預設值。
使用字典的其他方法
字典還提供了其他有用的方法,如 keys(), values(), 和 items()。
print(spot_rate.keys()) # 輸出:dict_keys(['amount', 'currency'])
print(spot_rate.items()) # 輸出:dict_items([('amount', '361.56'), ('currency', 'EUR')])
print(spot_rate.values()) # 輸出:dict_values(['361.56', 'EUR'])
內容解密:
keys()方法傳回一個包含字典所有鍵的檢視物件。items()方法傳回一個包含字典所有鍵值對的檢視物件,每個鍵值對以元組形式表示。values()方法傳回一個包含字典所有值的檢視物件。
將列表轉換為字典
我們可以使用 dict() 函式將一個由二元組組成的列表轉換為字典。
rate_as_list = list(spot_rate.items())
print(rate_as_list) # 輸出:[('amount', '361.56'), ('currency', 'EUR')]
print(dict(rate_as_list)) # 輸出:{'amount': '361.56', 'currency': 'EUR'}
內容解密:
- 將
spot_rate字典的鍵值對轉換為一個列表rate_as_list。 - 使用
dict()函式將rate_as_list轉換回字典。