P03-List and Dictionary#

當然,以下是中文版本的資料結構說明:

Objectives

List(串列)

  • 串列的創建與定義:使用方括號 [] 來定義一個串列。

    • 例如:my_list = [1, 2, 3]

  • 串列的屬性

    • 有序(Ordered)

    • 可索引(Indexable)

    • 可變(Mutable)

  • 串列操作

    • 增加元素:append(), extend(), insert()

    • 刪除元素:remove(), pop(), del

    • 通過索引訪問:my_list[0](返回第一個元素)

  • 串列切片(Slicing):

    • 例如:my_list[1:3](返回索引為1和2的元素)

  • 巢狀串列(Nested List):

    • 例如:nested_list = [[1, 2], [3, 4]]

Dictionary(字典)

  • 字典的創建與定義:使用大括號 {} 來定義一個字典。

    • 例如:my_dict = {'key1': 'value1', 'key2': 'value2'}

  • 字典的屬性

    • 無序(Unordered)

    • 以鍵值對(Key-Value Pairs)的形式儲存

    • 可變(Mutable)

  • 巢狀字典(Nested Dict):

    • 例如:nested_dict = {'outer_key': {'inner_key': 'value'}}

  • 字典函數

    • .keys():返回所有鍵

    • .values():返回所有值

    • .items():返回所有鍵值對

    • .get():取得指定鍵的值

    • .update():更新字典

  • 字典的替代方案

    • defaultdict():自動創建不存在的鍵並賦予預設值

    • Counter():計數物件的頻率

Practice: YouBike data

  1. read data from url

  2. convert data to json format

  3. extract data from json format

  4. convert data to list and dict

Link to useful data

List#

在Python程式語言中,List(串列) 是一種有序集合(ordered collection)。List能夠容納多種不同型態的元素,包括數值、字串、其他資料結構,甚至是函式。每個元素都會被指定一個從0開始的索引(index)。

定義與建立

一個List可以通過方括號([])來定義,並可用等號(=)賦值給一個變數。方括號內的元素由逗號(,)分隔。

empty_list = []  # 空的List
number_list = [1, 2, 3]  # 數值型List
mixed_list = [1, "two", 3.0]  # 混合型態的List

List的特性

  1. 有序性(Ordered): List的元素有明確的順序。

  2. 可索引存取(Indexed): 可以用索引來存取List中的元素,從頭(正數索引)或尾(負數索引)。

  3. 可變性(Mutable): List的內容可以被修改,包括增加、刪除和更新元素,因而不能當成Dict(字典)的鍵(key)。

常用操作

  • 新增元素

    • .append(element): 在List尾部增加單一元素。

    • .extend([elements]): 在List尾部增加多個元素。

  • 刪除元素

    • .pop(): 移除並返回List尾部的元素。

  • 索引存取

    • list[index]: 存取特定索引位置的元素。

my_list = [1, 2, 3]
print(my_list)

my_list.append(4)  # [1, 2, 3, 4]
print(my_list)

my_list.extend([5, 6])  # [1, 2, 3, 4, 5, 6]
print(my_list)

my_list.pop()  # 返回 6,my_list 變成 [1, 2, 3, 4, 5]
print(my_list)
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5]

資料型態的一致性

雖然List可以包含不同型態的元素,但在實際應用中,為了程式碼寫簡單和效能考量,通常會讓List內的所有元素型態保持一致。


Access a List#

在Python中,List的索引(Index)是從0開始的。以alist = [3, 4, 5]為例,元素3位於索引0的位置,因此可以用alist[0]來存取它。同理,元素4位於索引1的位置,可用alist[1]來存取。以下為基本規則:

  • 從頭開始索引:
    Python List的索引從0開始計數。

  • 最後元素的索引:
    若List的長度為n,則最後一個元素的索引將會是n - 1

  • 負數索引:
    alist[-1]可用來存取List的最後一個元素,alist[-2]則為倒數第二個元素,依此類推。

用途舉例:更新List內容

要修改List中特定索引的元素,您可以使用賦值運算符(=)來重新賦值。例如,alist[2] = 6會將索引2的元素更改為6

alist = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

# print whole list
print(alist)

# print the last one element
print(alist[-1])

# print the first and second elements
print(alist[0], alist[1])

# print the length of the list by function len()
print(len(alist))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1
10 9
10

Slice a List#

基本操作 在Python的List中,切片(Slicing)是一個非常實用的特性,讓我們能夠取出List中一段連續(或不連續)的元素。基本語法是alist[start:stop:step],其中start是起始索引,stop是結束索引(不包括此索引),step是步進值。以下為基本用法:

  • 從索引 1 到 2:
    alist[1:3] 會取得索引從1到2(不包含3)的元素,因此會取得alist中索引為1和2的元素。

  • 倒數第三個到倒數第一個:
    alist[-3:-1] 會取得從倒數第三個到倒數第二個的元素(注意這裡不包含倒數第一個)。

  • 前三個元素:
    alist[:3] 會取得alist中前三個元素,即索引0、1、2。

  • 除了最後三個以外的所有元素:
    alist[:-3] 會取得直到倒數第四個元素的所有元素。

  • 從第三個元素到最後:
    alist[3:] 會取得從索引3開始到最後的所有元素。

  • 從倒數第三個元素到最後:
    alist[-3:] 會取得從倒數第三個元素到最後的所有元素。

進階用法

  • 每隔一個(step=2)的切片:
    alist[::2] 會取得索引為0、2、4…的元素。

  • 反轉List:
    alist[::-1] 會得到一個完全反轉的新List。

# print the 1 to 2 elements
alist = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
print(alist[0:2])
print(alist[:2])


# print the last 3 to 2 elements from the end
print(alist[-3:-1])

print(alist[:3])
print(alist[:-3])

print(alist[3:])
print(alist[-3:])
[10, 9]
[10, 9]
[3, 2]
[10, 9, 8]
[10, 9, 8, 7, 6, 5, 4]
[7, 6, 5, 4, 3, 2, 1]
[3, 2, 1]

Update List:append(), extend() and + operator#

在Python中,更新List的元素可以透過多種方式,其中append()extend()+操作符是常用的方法。雖然這三種操作在名稱或用法上可能相似,但它們有著不同的用途和效果。

append() 方法#

  • append()用於在List的末尾追加一個元素。如果您追加的是另一個List,那麼這個List會作為單一元素添加到原List的末尾。

alist = [1, 2, 3]
blist = [3, 2, 1]

alist.append(2) # 結果會是 [1, 2, 3, 2]
print(alist) #

alist.append(blist)  
print(alist) # 結果會是 [1, 2, 3, 2, [3, 2, 1]]
[1, 2, 3, 2]
[1, 2, 3, 2, [3, 2, 1]]

extend() 方法#

append()是把某一物件當成最後一個元素加入List的最末,但extend()則是用於合併兩個List,把第二個List的所有元素追加到第一個List的末尾。

alist = [1, 2, 3]
blist = [3, 2, 1]
alist.extend(blist)  # 結果會是 [1, 2, 3, 3, 2, 1]
print(alist)

alist.append(blist)
print(alist)
[1, 2, 3, 3, 2, 1]
[1, 2, 3, 3, 2, 1, [3, 2, 1]]

+ 操作符#

使用+操作符也能達到合併List的效果,但其不像append()extend()操作後就會更改內容,其不會更改原有的alist,必須要透過重新賦值給alist才能達到相同的效果。

結論

  • alist.extend(blist) 會修改原來的 alist,使其包括 blist 的所有元素。

  • alist = alist + blistalist += blist 會有相同的效果,也就是合併兩個List,但這需要重新賦值給 alist

alist = [1, 2, 3]
blist = [3, 2, 1]

print(alist + blist)
print(alist)

alist.extend(blist)
print(alist)
[1, 2, 3, 3, 2, 1]
[1, 2, 3]
[1, 2, 3, 3, 2, 1]

Access Nested List#

在Python中,List不僅可以存儲基本數據類型(如整數、浮點數和字符串)的元素,還可以存儲其他的資料結構,包括另一個List。當一個List內部包含另一個List時,我們稱之為「雙層List」或「嵌套(Nested)List」。

存取嵌套元素: 存取雙層List的元素稍微複雜一點,因為每個元素本身也可能是一個List。例如,我們有一個雙層List nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

  • 存取第一個子List可以用 nested_list[0],這會返回 [1, 2, 3]

  • 如果要存取子List中的具體一個元素,比如第一個子List中的第二個元素(也就是2),則可以使用 nested_list[0][1]

多維索引: 在雙層或多層嵌套List中,你可以透過多維索引來存取元素。例如,nested_list[0][1] 中的第一個索引 0 是外層List的索引,第二個索引 1 是內層List的索引。

更新嵌套List: 更新雙層List的元素與單層List相似,只是你需要指定更多的索引層級。例如,如果你想將上面例子中的數字2改為20,可以這樣做:nested_list[0][1] = 20

遍歷雙層List: 雙層List也可以使用嵌套的迴圈來遍歷。例如,

for sublist in nested_list:
    for element in sublist:
        print(element)
mlist = [[1, 2, 3, 4, 5, 6, 7],
         [11, 12, 13, 14, 15, 16, 17],
         [21, 22, 23, 24, 25, 26, 27]]
print(mlist[2][5])
26
alist = [1, 2, 3, 4, 'abc', '123', '123.1', [1, 2, 3] ]
print(len(alist)) #length len()
print(alist[7], type(alist[7]))


# Access any elements in the second layers of alsit
print(alist[-1][2]) # Review: -1 indicates the last element in a list
8
[1, 2, 3] <class 'list'>
3

Dictionary(字典)#

在Python中,Dictionary(通常簡稱為dict)是一種極為靈活並廣泛使用的數據結構。dict是用於存儲鍵(key)與值(value)對應的集合。與List不同,dict的元素並不是按照順序來排列的,因此我們不能用索引(index)來存取元素,而是要用相應的鍵。

  1. 基本語法:
    例如,考慮以下的dict,在這個例子中,你可以通過鍵來存取相對應的值。adict[1] 會返回 3,而 adict['2'] 會返回 4

    adict = {1: 3, '2': 4}
    
  2. 鍵的不可變性(Immutable)
    Dict的鍵可以是多種不同的數據類型,例如整數、浮點數、字符串等。然而,有一些數據類型(如List和另一個dict)是不允許作為鍵的。這是因為這些數據類型是「可變」的,意即你可以在任何時間點修改它們的內容,這可能會導致數據不一致或錯誤。

  3. 值的多樣性
    Dict的值(value)可以是任何數據類型,包括基本類型(如整數、字符串)和其他的數據結構(如List、Tuple、另一個dict等)。

  4. Nested Dictionary
    Dict中的值也可以是另一個dict,這種結構稱為「Nested (嵌套)Dictionary」。例如,在這個例子中,nested_dict['key1'] 會返回一個dict:{'subkey1': 1, 'subkey2': 2}

    nested_dict = {'key1': {'subkey1': 1, 'subkey2': 2}, 'key2': {'subkey3': 3}}
    

其他注意事項

  • Tuple可以作為dict的鍵,因為它是不可變的(Immutable);List不可以作為dict的鍵,因為它是可變的(Mutable)。

  • 使用 .get() 方法來存取鍵可以避免引發KeyError。例如,adict.get('key', 'default_value')。但我們通常會使用 adict['key'] 來存取鍵,因為這樣可以讓程式碼更簡潔。

  • 使用 .keys().values() 方法,你可以分別獲取dict中所有的鍵和值。

  • update() 方法可以用來合併兩個dict。例如,adict.update(bdict) 會將 bdict 中的所有鍵值對合併到 adict 中。

adict = {1:3, '2':4}
print(adict[1], adict['2'])
alist = [1, 2, 3, "a", 'b', 'c', [1, 2, 3], [4, 5, 6]]
print(len(alist))

adict = {'3':3, 2:4, '3':5, '4':6, 1.13: '123', 'a': alist}
print(len(adict))
print(type(adict))
print(adict['a'][6][1])

bdict = {1:alist, 2:adict}
print(bdict[2]['a'][6][1])
3 4
8
5
<class 'dict'>
2
2

Accessing dictionary#

在Python中,存取Dictionary(或簡稱為dict)的元素主要是通過其對應的鍵(key)來實現。以下是一些常見的操作方式:

  1. 印出整個字典:使用print()函數可以輸出整個字典的內容。例如以下例子會會列印出bdict字典中所有的鍵值對。

    print(bdict)
    
  2. 使用鍵來存取值:如果你知道某個鍵(key),就可以直接用這個鍵來獲取其對應的值(value)。下例這將會打印出與鍵 '3' 相關聯的值。

    print(adict['3'])
    
  3. 獲取所有鍵和值:你可以使用 .keys().values() 方法來分別獲取所有的鍵和值。下例.keys() 將返回一個包含所有鍵的列表,而 .values() 將返回一個包含所有值的列表。

    print(adict.keys())
    print(adict.values())
    
# access the dictionary

# print whole dict
print(bdict)

# print the value of key '1'
print(adict['3'])

# print the value of key 1
print(adict.keys())
print(adict.values())

# print the first element of the list mapped by key 4
{1: [1, 2, 3, 'a', 'b', 'c', [1, 2, 3], [4, 5, 6]], 2: {'3': 5, 2: 4, '4': 6, 1.13: '123', 'a': [1, 2, 3, 'a', 'b', 'c', [1, 2, 3], [4, 5, 6]]}}
5
dict_keys(['3', 2, '4', 1.13, 'a'])
dict_values([5, 4, 6, '123', [1, 2, 3, 'a', 'b', 'c', [1, 2, 3], [4, 5, 6]]])

Counter() and defaultdict()#

有兩個資料結構與dict非常近似,一個是 defaultdict(),另一個是 Counter()

defaultdict()#

defaultdict() 是一個非常方便的資料結構,來自於 collections 模塊。它的主要優點是當你試圖存取一個不存在的鍵時,它會自動初始化這個鍵,而不會引發 KeyError。在創建 defaultdict 的時候,你可以傳入一個工廠函數,用於生成預設值。例如:

from collections import defaultdict

adict = defaultdict(list)
adict['a'].append(1)
adict['b'].append(2)
adict['a'].append(3)
print(adict)

bdict = defaultdict(int)
bdict['a'] += 1
bdict['b'] += 1
print(bdict)
defaultdict(<class 'list'>, {'a': [1, 3], 'b': [2]})
defaultdict(<class 'int'>, {'a': 1, 'b': 1})

defaultdict()dict 的比較::使用普通的 dict,你需要手動檢查鍵是否存在或使用 setdefault 方法:

d = {}
d.setdefault('a', []).append(1)

Counter()#

  • Counter 是另一個來自於 collections 模塊的資料結構,專門用於計數。它非常適合於頻率計數等應用。

  • Counter可以使用 most_common(n) 方法來獲取出現次數最多的 n 個元素。

  • Counterdict 的比較:
    使用普通的 dict 進行計數會相對複雜,需要檢查每個鍵是否已經存在於字典中:

from collections import Counter

c = Counter(['a', 'b', 'c', 'a', 'b', 'a'])
print(c)

print(c.most_common(2))
Counter({'a': 3, 'b': 2, 'c': 1})
[('a', 3), ('b', 2)]