zip 在英文是拉鍊的意思,而 Python 的內建函式 zip()
就是取名自拉鍊的形象,它可以同時迭代多個 list、每次分別從各個 list 中取一個元素配成同一組,彷彿拉鍊齒一組一組整齊對應的樣子。
這則筆記中,好豪將跟你分享 zip()
函式有哪些好用的地方,我會先簡短教學 zip()
函式的功能,並把重點放在能讓你快速活用的三項實戰案例:
- 矩陣轉置:用 Python 解 LeetCode 演算法題目常用技巧
- dict 反轉:NLP 資料分析建立詞庫
- 同時迭代多個長短不一的 list:
zip()
的進階用法
快速學習 zip 函式
zip()
:從每個引數中的可迭代物件,組合各個可迭代物件的元素,產生一個迭代器
Python3 官方文件
在 Python3,zip()
會回傳一個迭代器(iterator),而這個迭代器的第 i 個元素,就是每個傳入引數的第 i 個內容所組合成的 tuple。zip()
的引數需要是可迭代物件,包括常用的 list、set、dict、或者 str 等等。請看以下範例:
>>> type(zipped) # 回傳的是一個 'zip' 物件,它是可迭代的
<zip object at 0x108e8bc80>
# 也可用 list() 或 set() 將迭代器轉換成其他資料型態
[('a', 1), ('b', 2), ('c', 3)]
{('c', 3), ('b', 2), ('a', 1)}
## 函式引數需要是可迭代物件,因此也包括 str, set, dict 等等
>>> z = {'first': 0.5, 'second': 0.6, 'third': 0.7}
>>> for i in zip(x, y, z):
## 1. 如果你 zip 遍歷內容是 set,要注意它沒有排序的特性
## 本例 set 的 print 的順序就跟宣告內容順序不同
## 2. 當你直接遍歷 dict 物件的時候,只會遍歷 key、而不是 value 喔
## 如果需要遍歷 dict 的 value,可以使用 .values() 函式
>>> for i in zip(x, y, z.values()):
究竟使用 zip()
函式有什麼好處呢?我們直接從以下三個實戰案例來學習。
矩陣轉置
給定一個 3*2 的矩陣(在 Python 內的資料結構可以用 3 個 list、各有 2 個元素來表示),要如何轉置成 2*3 的矩陣呢?
矩陣轉置只需要使用 zip(*matrix)
這個簡單技巧就能完成:
>>> for i in zip(*matrix):
## 如果轉置後希望維持原本 list of lists 的結構
>>> [list(row) for row in zip(*matrix)]
原理說明:用 *
符號解包
呼叫函式的時候,如果在可迭代物件的引數前面加上 *
符號,可以進行解包(unpack):將可迭代物件內的每個內容物各自成為引數。
- 如果呼叫
func(my_list)
,函式只會收到一個引數 - 如果呼叫
func(*my_list)
,該 list 長度多長、函式就收到幾個引數
在轉置矩陣的範例中,矩陣解包後,每一列各自成為一個引數傳入 zip()
了。
>>> for i in zip(*matrix):
>>> for i in zip([1, 2], [3, 4], [5, 6]):
LeetCode 實戰
此範例來自 LeetCode 477 (難度 Medium),筆者在此只探討用到 zip()
解題的部分。
Hamming Distance 是要計算兩個二進位制的數總共有幾個位元(bit)不一樣,而本題是需要你計算多個數的 Hamming Distance 總和。例如,要計算 1、7、與 10 三個數在二進位制總共有幾個位元不同。
如何一個個位元比較呢?用 zip()
處理二進位制字串就很好解決了!
>>> arr = [f"{i:04b}" for i in arr]
zip()
幫你把各個數的同一個位元都整齊放在同一列了,接下來你只要專注想出每一列要怎麼加總位數差異就可以了!
如果這題解完你還覺得練習不夠,和你分享 LeetCode 這題 也適合用 zip()
解題。
dict 反轉
處理資料的時候,zip()
可以用來將 dict 的 key 與 value 關係反轉。
>>> my_dict = {'a': 1, 'b': 2, 'c': 3}
dict_keys(['a', 'b', 'c'])
dict_items([('a', 1), ('b', 2), ('c', 3)])
>>> {value: key for key, value in my_dict.items()}
>>> dict(zip(my_dict.values(), my_dict.keys()))
上面提了兩種方式做到 dict 的 key 與 value 反轉,讀者可以依照自己的寫程式風格選擇要用哪種。
運用場景:NLP 建立詞庫
在做 NLP 文字分析時,會需要建立詞庫,文字要編成索引編號(index)才能輸入到機器學習模型,又因為模型輸出也是索引編號,所以我們還需要將索引編號轉換回來原本的文字。
要同時做到 文字 -> index
以及 index -> 文字
,基本作法就是創造兩個 key 與 value 相反的 dict。
>>> sentence = "My name is haohao. This is my blog."
>>> sentence_list = [w.lower().strip('.') for w in sentence.split()]
['my', 'name', 'is', 'haohao', 'this', 'is', 'my', 'blog']
>>> set(sentence_list) # 用 set 去除重複字詞
{'is', 'name', 'this', 'haohao', 'my', 'blog'}
>>> word2index = {w: i for i, w in enumerate(set(sentence_list))}
{'is': 0, 'name': 1, 'this': 2, 'haohao': 3, 'my': 4, 'blog': 5}
## 再用 zip() 反轉 dict,建立 `index -> 文字` 的對應關係
>>> index2word = dict(zip(word2index.values(), word2index.keys()))
{0: 'is', 1: 'name', 2: 'this', 3: 'haohao', 4: 'my', 5: 'blog'}
## 可以任意讓資料在 `文字` 與 `index` 之間自由切換囉!
['my', 'name', 'is', 'haohao', 'this', 'is', 'my', 'blog']
>>> sentence_index_list = [word2index[w] for w in sentence_list]
>>> [index2word[i] for i in sentence_index_list]
['my', 'name', 'is', 'haohao', 'this', 'is', 'my', 'blog']
NLP 詞庫建立,通常還需要考慮詞頻等問題,可以參考 Udacity 課程教學的簡單詞庫寫法(Github)。如果對入門 NLP 與深度學習有更多興趣,推薦你閱讀筆者好豪 學習這門 Udacity 深度學習 Pytorch 課程的心得,一起來上課!
同時迭代多個長短不一的 list
請注意:如果你傳給 zip()
的多個可迭代物件長度不一樣,zip()
不會回報 error,而是走訪完最短的可迭代物件就結束。
如果你沒察覺到傳入的多個 list 長短不一,長度較長的 list 數值就會在走訪時被遺漏!
# 範例:多個 list 長短不一,又想用 zip() 同時走訪
>>> list_b = ['a', 'b', 'c', 'd', 'e']
>>> list_c = [0.5, 0.7, 0.9]
>>> for i in zip(list_a, list_b, list_c):
## 使用 zip() 之後只剩下走訪 2 個元素!
## 如果 zip() 引數之中混入一個長度為 0 的 list
>>> for i in zip([], list_a, list_b, list_c):
>>> len(list(zip([], list_a, list_b, list_c)))
如果你需要以最長的 list 為準、用 zip()
同時走訪多個 list,可以把 zip()
替換成 itertools
內建函式庫 裡的 zip_longest()
函式:
>>> from itertools import zip_longest
>>> for i in zip_longest(list_a, list_b, list_c):
## 上例中,較短的 list 先被走訪完畢,之後都預設填補 None
## 設定 zip_longest() 的 fillvalue 引數可以選擇填補其他內容
>>> for i in zip_longest(list_a, list_b, list_c, fillvalue="NO_VALUE"):
('NO_VALUE', 'd', 'NO_VALUE')
('NO_VALUE', 'e', 'NO_VALUE')
## 要用 zip() 同時走訪長短不一的 list、並且要把最長的 list 走訪完
## - cycle(): 較短的 list 被走訪完後,會從頭再走訪、不斷重複
>>> from itertools import cycle, repeat
>>> for i in zip(cycle(list_a), list_b, cycle(list_c), repeat("ʕ •ᴥ•ʔ")):
## 想更加了解 itertools 的讀者,可以參考官方文件
## https://docs.python.org/zh-cn/3/library/itertools.html
結語
在這則筆記裡,我們學會了:
zip()
的基本用法- 矩陣轉置
- dict 反轉
- 還有長短不一的 list 同時迭代進階技巧
zip()
函式不只方便好用,筆者好豪也常在網友分享的 面試題 看到與它相關的問題出現,可以說是邁向進階 Python 必學的內建函式,相信讀者掌握這則筆記的技巧後,不論是刷演算法題或資料分析工作,都能多增加一項寫 Python 利器!
參考資料:
還想知道更多 Python 相關技巧嗎?推薦你閱讀好豪蒐集的《Python 神乎其技》免費教學文章,學會更多 Pythonic Code!
如果這篇文章有幫助到你,歡迎追蹤 好豪的粉絲專頁,我會持續分享 Python 技巧、資料科學等知識。
也歡迎點選下方按鈕將本文加入書籤隨時複習、或者分享給更多正在學 Python 的朋友。
from: https://haosquare.com/python-zip-function/
No comments:
Post a Comment