🍀Tóm tắt buổi trước
Okay bữa trước chúng ta học xong về NumPy dù là cần phải thực hành thêm thì chúng ta mới quen hơn. Nhưng không sao chúng ta cứ đi những kiến thức cơ bản cho đầy đủ rồi ráp vào các bài toán cụ thể thì sẽ dễ nhớ hơn
Bạn có thể coi lại bài trước của mình:
🔴 Buổi Live ngày 11:
🍀Khái quát về thư viện pandas
pandas chứa các cấu trúc dữ liệu kèm công cụ thao tác với dữ liệu để làm sạch và phân tích dữ liệu nhanh chóng và thuận tiện.
Nó thường được kết hợp sử dụng với các thư viện khác tùy vào mục đích như để 1tính toán đại số: NumPy và SciPy, để 2phân tích: statsmodels và scikit-learn và các thư viện 3trực quan hóa dữ liệu: matplotlib.
Có thể bạn “đã biết” thì pandas thừa hưởng tính năng đặc trưng của NumPy là tính toán-dựa trên mảng array-based computing, bên cạnh đó là sự bổ sung của các hàm đặc biệt xử lý trên mảng.
Mặc dù pandas sử dụng nhiều quy ước mã hóa từ NumPy, nhưng điểm khác biệt lớn nhất là pandas được thiết kế để làm việc với dữ liệu bảng hoặc dữ liệu không đồng nhất. Ngược lại, NumPy phù hợp nhất để làm việc với dữ liệu mảng số có kiểu đồng nhất.
Kể từ khi trở thành một dự án mã nguồn mở vào năm 2010, pandas đã phát triển thành một thư viện khá lớn được áp dụng trong một tập hợp rộng lớn các trường hợp sử dụng trong thế giới thực. Cộng đồng phát triển đã phát triển lên hơn 2.500 cộng tác viên riêng biệt, những người đã giúp xây dựng dự án khi họ sử dụng nó để giải quyết các vấn đề dữ liệu hàng ngày của mình. Cộng đồng phát triển và người dùng pandas sôi động là một phần quan trọng trong thành công của nó.
Convention code: Trong suốt những buổi còn lại thì mình sẽ tuân theo một mẫu import thư viện như sau – đây cũng được xem là tiêu chuẩn trong cộng động:
In [1]: import numpy as np
In [2]: import pandas as pdNên sau này nếu bạn thấy pd nghĩa là nó đang tham chiếu tới pandas. Đôi khi một số trường hợp bạn sẽ thấy chúng ta chỉ sử dụng một vài module từ thư viện được thêm vào local namespace vì chúng ta chỉ cần bao nhiêu đó thôi (tối giản):
In [3]: from pandas import Series, DataFrame🍀 Các cấu trúc dữ liệu của pandas
Trong pandas có hai cấu trúc dữ liệu “cây nhà lá vườn” của nó mà ta nên làm quen trước khi đi xa hơn: Series và DataFrame.
Tất nhiên như mọi cấu trúc dữ liệu khác, tụi nó không phải là giải pháp toàn vẹn cho mọi vấn đề nên không hẳn lúc nào dân Data cũng lưu dữ liệu vào nó để tiếp tục xử lý ở các bước tiếp theo. Nhưng ít nhất thì tụi nó cho chúng ta nhiều cách xử lý đáng tin cậy cho khá nhiều các tác vụ dữ liệu khác nhau.
🚀Series
Bản chất là một mảng 1-chiều các dữ liệu chung kiểu và gắn với mảng các nhãn dữ liệu index (nó cũng là một sequence object và khá tương tự với kiểu NumPy)
Điểm đặc biệt trong Series khác với index trong các cấu trúc dữ liệu khác là ta có thể thay đổi không dùng hệ 0-index.
Note
In [14]: obj = pd.Series([4, 7, -5, 3])
In [15]: obj
Out[15]:
0 4
1 7
2 -5
3 3
dtype: int64🔥 PandasArray object
Phần in ra màn hình cho thấy cột bên trái là index còn bên phải tương ứng là giá trị. Mặc định khi ta không định nghĩa trước thì sẽ đánh index từ 0 tới N-1 với N là độ dài của dữ liệu. Bạn có thể lấy riêng ra các đối tượng index hay value bằng thuộc tính tương ứng index và array:
In [16]: obj.array
Out[16]:
<PandasArray>
[4, 7, -5, 3]
Length: 4, dtype: int64
In [17]: obj.index
Out[17]: RangeIndex(start=0, stop=4, step=1)Kết quả trả về của thuộc tính .array là một PandasArray – một đối tượng về bản chất chứa một mảng NumPy, mà tính chất của mảng NumPy là đồng nhất về kiểu dữ liệu.
Vấn đề ở đây là nếu mảng đó có một giá trị Null thì sao? May sao gần đây thì pandas có phát triển các loại dữ liệu mở rộng giúp linh hoạt hơn.
🔥 labels thay vì index
❗ Lưu ý: Bản thân nhãn hay chỉ mục cũng đều mang ý nghĩa giống nhau nhưng mình muốn dùng riêng hai tên này để phân biệt 0-index và text-index
Mặc định label của Series theo 0-index nhưng ta có thể tuỳ biến gắn từng giá trị với các nhãn khác nhau
In [18]: obj2 = pd.Series([4, 7, -5, 3], index=["d", "b", "a", "c"])
In [19]: obj2
Out[19]:
d 4
b 7
a -5
c 3
dtype: int64
In [20]: obj2.index
Out[20]: Index(['d', 'b', 'a', 'c'], dtype='object')Để cho dễ hình dung ta có thể xem Series giống một Dictionary nhưng cố định độ dài và có sắp xếp thứ tự,
Note
Bạn cũng hoàn toàn có thể thay đổi trực tiếp index bằng labels
🔥 index hoặc indices
So sánh với mảng NumPy, nhờ tính chất tương đồng với dictionary nên ta khá linh hoạt khi chọn các giá trị đơn lẻ hoặc nhóm các nhãn:
In [21]: obj2["a"]
Out[21]: -5
In [22]: obj2["d"] = 6
In [23]: obj2[["c", "a", "d"]]
Out[23]:
c 3
a -5
d 6
dtype: int64Ở đây ["c", "a", "d"] được hiểu là một danh sách các chỉ mục, ngay cả khi nó chứa các chuỗi thay vì các số nguyên.
🔥 Liên kết index-value
Thực hiện tính toán biến đổi bằng các hàm NumPy hoặc các toán tử NumPy, như lọc theo mảng Boolean, tích vô hướng hoặc áp dụng các hàm toán học, vẫn giữa nguyên liên kết giữa nhãn-giá trị
In [24]: obj2[obj2 > 0]
Out[24]:
d 6
b 7
c 3
dtype: int64
In [25]: obj2 * 2
Out[25]:
d 12
b 14
a -10
c 6
dtype: int64
In [26]: import numpy as np
In [27]: np.exp(obj2)
Out[27]:
d 403.428793
b 1096.633158
a 0.006738
c 20.085537
dtype: float64🔥 Từ khóa “in”
Ta cũng có những cú pháp giống với dictionary:
In [28]: "b" in obj2
Out[28]: True
In [29]: "e" in obj2
Out[29]: False🔥 Ép kiểu “dict” thành “series”
Điều thú vị là ta hoàn toàn có thế biến dictionary thành series:
In [30]: sdata = {"Ohio": 35000, "Texas": 71000, "Oregon": 16000, "Utah": 5000}
In [31]: obj3 = pd.Series(sdata)
In [32]: obj3
Out[32]:
Ohio 35000
Texas 71000
Oregon 16000
Utah 5000
dtype: int64Và ta cũng có chiều ngược lại
In [33]: obj3.to_dict()
Out[33]: {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}🔥 Sắp xếp lại thứ tự nhãn
Bạn có thể thay đổi vị trí nhãn bằng tham số index. Nhưng nếu truyền vào một nhãn không tồn tại trong danh sách các khóa của dictionary thì nó sẽ trả về NaN (Not a Number) – đánh dấu là giá trị missing hoặc NA trong pandas.
In [34]: states = ["California", "Ohio", "Oregon", "Texas"]
In [35]: obj4 = pd.Series(sdata, index=states)
In [36]: obj4
Out[36]:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64🔥 Làm việc với giá trị rỗng
Hai hàm isna và notna trong pandas để xác định các giá trị NA
In [37]: pd.isna(obj4)
Out[37]:
California True
Ohio False
Oregon False
Texas False
dtype: bool
In [38]: pd.notna(obj4)
Out[38]:
California False
Ohio True
Oregon True
Texas True
dtype: boolBản thân đối tượng cũng có phương thức thực thể (instance methods) isna của riêng nó:
In [39]: obj4.isna()
Out[39]:
California True
Ohio False
Oregon False
Texas False
dtype: boolVì bản thân việc xử lý dữ liệu rỗng là một chủ đề rộng nên chúng ta sẽ còn quay trở lại vài kỳ sau.
🔥 Data alignment – Join trong Series
Có một đặc tính hay ho của Series là nó tự động bắt cặp các nhãn giống nhau khi tính toán giữa các Series:
In [40]: obj3
Out[40]:
Ohio 35000
Texas 71000
Oregon 16000
Utah 5000
dtype: int64
In [41]: obj4
Out[41]:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
In [42]: obj3 + obj4
Out[42]:
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN
dtype: float64Tính chất này có nét tương đồng với cách hoạt động của toán tử JOIN khi làm việc với CSDL
Note
🔥 Thuộc tính “name”
Cả đối tượng Series hay thuộc tính index của nó đều có một thuộc tính con là name mà ta sẽ dùng tới ở các chức năng khác của pandas. Ở đây trước mắt ta gán name cho 2 đối tượng trên:
In [43]: obj4.name = "population"
In [44]: obj4.index.name = "state"
In [45]: obj4
Out[45]:
state
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
Name: population, dtype: float64🚀 DataFrame
DataFrame biểu diễn một bảng dữ liệu “hình chữ nhật” – Ta hay gọi là tabular, chứa tập hợp các cột được đặt tên và có thứ tự, mỗi cột có thể có một kiểu giá trị khác nhau (số, chuỗi, Boolean, v.v.). DataFrame có cả chỉ mục hàng và cột; nó có thể được coi là một “dictionary của các Series có cùng một chỉ mục“.
Mặc dù DataFrame về mặt vật lý là hai chiều, bạn có thể sử dụng nó để biểu diễn dữ liệu nhiều chiều hơn trong định dạng tabular bằng cách sử dụng chỉ mục phân cấp (
"hierachical indexing")
🔥 Cách khởi tạo
Có nhiều cách để tạo DataFrame, mặc dù một trong nhưng cách phổ dụng nhất là từ dictionary có các key kích thước giống nhau hoặc từ một mảng NumPy
data = {"state": ["Ohio", "Ohio", "Ohio", "Nevada", "Nevada", "Nevada"],
"year": [2000, 2001, 2002, 2001, 2002, 2003],
"pop": [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)Kết quả ở trên cho ra DataFrame với thứ tự các cột tương ứng với thứ tự các khóa trong dictionary (giống như Series các chỉ mục được tự động gán qua).
In [50]: frame
Out[50]:
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2🔥 Truy vấn dữ liệu từ DataFrame
Một hoạt động chúng ta thường hay làm là Data Profiling, và với Python chúng ta có một số hàm giúp ta làm việc này
📑 Phương thức head chọn ra 5 dòng đầu tiên của DataFrame
In [51]: frame.head()
Out[51]:
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9📑 Tương tự tail trả về 5 dòng cuối
In [52]: frame.tail()
Out[52]:
state year pop
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2📑 Nếu bạn chỉ định một số cột nhất định khi truyền vào tham số columns thì ta nhận được tương ứng số cột với thứ tự được sắp xếp
In [53]: pd.DataFrame(data, columns=["year", "state", "pop"])
Out[53]:
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
5 2003 Nevada 3.2📑 Nếu ta lấy một cột không tồn tại thì ta nhận được cột đó chứa các giá trị rỗng (NaN)
In [54]: frame2 = pd.DataFrame(data, columns=["year", "state", "pop", "debt"])
In [55]: frame2
Out[55]:
year state pop debt
0 2000 Ohio 1.5 NaN
1 2001 Ohio 1.7 NaN
2 2002 Ohio 3.6 NaN
3 2001 Nevada 2.4 NaN
4 2002 Nevada 2.9 NaN
5 2003 Nevada 3.2 NaN
In [56]: frame2.columns
Out[56]: Index(['year', 'state', 'pop', 'debt'], dtype='object')📑 Một cột còn có thể được lấy lên bằng 2 phương pháp:
- dictionary-like notation (dùng ký hiệu truy vấn khóa)
- dot attribute notation (chấm gọi thuộc tính)
In [57]: frame2["state"]
Out[57]:
0 Ohio
1 Ohio
2 Ohio
3 Nevada
4 Nevada
5 Nevada
Name: state, dtype: object
In [58]: frame2.year
Out[58]:
0 2000
1 2001
2 2002
3 2001
4 2002
5 2003
Name: year, dtype: int64Trong ví dụ trên
Note 💡frame2[column]có thể thực hiện với bất cứ cột nào nhưngframe2.columnsẽ báo lỗi nên tên cột không đúng theo quy tắc như có khoảng trắng (thay bằng_) hoặc ký hiệu đặc biệt, thậm chí nếu tên giống với các phương thức của DataFrame
❗ Lưu ý: rằng kết quả trả về là Series có cùng chỉ mục giống DataFrame và thuộc tính name được tự động gán bằng tên cột.
📑 Bạn có thể truy xuất theo tên hoặc theo vị trí cột bằng hai thuộc tính đặc biệt loc và iloc
In [59]: frame2.loc[1]
Out[59]:
year 2001
state Ohio
pop 1.7
debt NaN
Name: 1, dtype: object
In [60]: frame2.iloc[2]
Out[60]:
year 2002
state Ohio
pop 3.6
debt NaN
Name: 2, dtype: object📑 DataFrame có thể xem là mutuble object vì ta có thể sử đổi dữ liệu bằng cách gán trực tiếp giá trị
In [61]: frame2["debt"] = 16.5
In [62]: frame2
Out[62]:
year state pop debt
0 2000 Ohio 1.5 16.5
1 2001 Ohio 1.7 16.5
2 2002 Ohio 3.6 16.5
3 2001 Nevada 2.4 16.5
4 2002 Nevada 2.9 16.5
5 2003 Nevada 3.2 16.5
In [63]: frame2["debt"] = np.arange(6.)
In [64]: frame2
Out[64]:
year state pop debt
0 2000 Ohio 1.5 0.0
1 2001 Ohio 1.7 1.0
2 2002 Ohio 3.6 2.0
3 2001 Nevada 2.4 3.0
4 2002 Nevada 2.9 4.0
5 2003 Nevada 3.2 5.0❗ Nếu gán bằng list hoặc array thì độ dài nhất định phải khớp với độ dài của DataFrame. Còn nếu gán bằng Series thì nhãn của nó tự động bắt với index của DataFrame, thậm chí ta còn cấu hình được thêm vào các vị trí xác định
In [65]: val = pd.Series([-1.2, -1.5, -1.7], index=[2, 4, 5])
In [66]: frame2["debt"] = val
In [67]: frame2
Out[67]:
year state pop debt
0 2000 Ohio 1.5 NaN
1 2001 Ohio 1.7 NaN
2 2002 Ohio 3.6 -1.2
3 2001 Nevada 2.4 NaN
4 2002 Nevada 2.9 -1.5
5 2003 Nevada 3.2 -1.7❗ Nếu gán vào một cột không tồn tại thì nó sẽ tự tạo ra một cột mới với giá trị và tên tương ứng
📑 Ta có thể dùng hàm del để xóa cột, khác chút là ta để nó giống như một keyword hơn là những cách gọi hàm thông thường
In [68]: frame2["eastern"] = frame2["state"] == "Ohio"
In [69]: frame2
Out[69]:
year state pop debt eastern
0 2000 Ohio 1.5 NaN True
1 2001 Ohio 1.7 NaN True
2 2002 Ohio 3.6 -1.2 True
3 2001 Nevada 2.4 NaN False
4 2002 Nevada 2.9 -1.5 False
5 2003 Nevada 3.2 -1.7 False
In [70]: del frame2["eastern"]
In [71]: frame2.columns
Out[71]: Index(['year', 'state', 'pop', 'debt'], dtype='object')Trong ví dụ trên ta tạo ra một cột Boolean mới tên “eastern” dựa trên logic cột “state” trả về True khi là “Ohio”.
- Trong ví dụ trên không thể dùng cú pháp
frame2.easternđể tạo cột mới như cách trênNote 💡
- Khi indexing (ví dụ bằng
ilochayloc) thì Series trả về là một view ánh xạ chính DataFrame đó chứ không phải bản copy nên việc sửa đổi sẽ thay đổi trực tiếp lên bản gốc
📑 Một dạng khác của data là nested dict của dict
In [72]: populations = {"Ohio": {2000: 1.5, 2001: 1.7, 2002: 3.6},
....: "Nevada": {2001: 2.4, 2002: 2.9}}Với trường hợp này thì pandas sẽ hiểu khóa của dict bên ngoài là cột còn khóa của dict bên trong là chỉ mục
In [73]: frame3 = pd.DataFrame(populations)
In [74]: frame3
Out[74]:
Ohio Nevada
2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9📑 Bạn có thể chuyển vị DataFrame (hoán chuyển dòng thành cột) với cú pháp tương tự một mảng NumPy
In [75]: frame3.T
Out[75]:
2000 2001 2002
Ohio 1.5 1.7 3.6
Nevada NaN 2.4 2.9Lưu ý rằng việc chuyển vị sẽ loại bỏ các kiểu dữ liệu cột nếu các cột không có cùng kiểu dữ liệu, vì vậy việc chuyển vị và sau đó chuyển vị lại có thể làm mất thông tin kiểu trước đó.
Các cột trở thành mảng các đối tượng Python thuần túy trong trường hợp này. (Là đối tượng không được định nghĩa bới bất kỳ lớp nào bởi người dùng)
Các khóa 2 dict ở trên tự động kết hợp để tạo thành index chung của DF mặc dù 1-trong-2 không có đầy đủ keys. Nhưng nếu index được chỉ định rõ ràng thì khác:
In [76]: pd.DataFrame(populations, index=[2001, 2002, 2003])
Out[76]:
Ohio Nevada
2001 1.7 2.4
2002 3.6 2.9
2003 NaN NaNTương tự với dict của Series:
In [77]: pdata = {"Ohio": frame3["Ohio"][:-1],
....: "Nevada": frame3["Nevada"][:2]}
In [78]: pd.DataFrame(pdata)
Out[78]:
Ohio Nevada
2000 1.5 NaN
2001 1.7 2.4
📑 Nếu index và columns (vốn là 2 Series) của DataFrame có khai báo thuộc tính name thì nó cũng sẽ được hiển thị:
In [79]: frame3.index.name = "year"
In [80]: frame3.columns.name = "state"
In [81]: frame3
Out[81]:
state Ohio Nevada
year
2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9Không giống Series, DataFrame không có thuộc tính name. Khi ta dùng hàm to_numpy sẽ trả về dữ liệu của nó nhưng được đặt trong ndarray 2-chiều:
In [82]: frame3.to_numpy()
Out[82]:
array([[1.5, nan],
[1.7, 2.4],
[3.6, 2.9]])Nếu các cột của DataFrame có các kiểu dữ liệu khác nhau, mảng 2-chiều trả về sẽ được chọn để chứa đủ thêm tên các cột
In [83]: frame2.to_numpy()
Out[83]:
array([[2000, 'Ohio', 1.5, nan],
[2001, 'Ohio', 1.7, nan],
[2002, 'Ohio', 3.6, -1.2],
[2001, 'Nevada', 2.4, nan],
[2002, 'Nevada', 2.9, -1.5],
[2003, 'Nevada', 3.2, -1.7]], dtype=object)🚀Index objects
“Các đối tượng chỉ mục” của pandas chịu trách nhiệm giữ các nhãn trục tọa độ (gồm các tên cột của DataFrame) và các metadata khác (như trên trục hoặc thuộc tính names).
Bất kỳ mảng hoặc labels sequence nào cũng có thể dùng để làm Index cho Series hoặc DataFrame thông qua tham số index.
In [84]: obj = pd.Series(np.arange(3), index=["a", "b", "c"])
In [85]: index = obj.index
In [86]: index
Out[86]: Index(['a', 'b', 'c'], dtype='object')
In [87]: index[1:]
Out[87]: Index(['b', 'c'], dtype='object')Index object là một immutable và không thể sửa đổi:
index[1] = "d" # TypeErrorTính chất immutability đảm bảo tính toàn vẹn khi ta dùng chung giữa các cấu trúc dữ liệu với nhau:
In [88]: labels = pd.Index(np.arange(3))
In [89]: labels
Out[89]: Index([0, 1, 2], dtype='int64')
In [90]: obj2 = pd.Series([1.5, -2.5, 0], index=labels)
In [91]: obj2
Out[91]:
0 1.5
1 -2.5
2 0.0
dtype: float64
In [92]: obj2.index is labels
Out[92]: TrueBổ sung thêm là khi đối tượng ở dạng mảng, đối tượng Index có các hành vi của một set cố định kích thước
In [93]: frame3
Out[93]:
state Ohio Nevada
year
2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9
In [94]: frame3.columns
Out[94]: Index(['Ohio', 'Nevada'], dtype='object', name='state')
In [95]: "Ohio" in frame3.columns
Out[95]: True
In [96]: 2003 in frame3.index
Out[96]: FalseNhưng không giống sets của Python, pandas Index có thể chứa các nhãn trùng lặp:
In [97]: pd.Index(["foo", "foo", "bar", "bar"])
Out[97]: Index(['foo', 'foo', 'bar', 'bar'], dtype='object')Mỗi đối tượng index có một số phương thức và thuộc tính logic để trả lời các câu hỏi liên quan tới dữ liệu nó chứa ví dụ is_unique , is_monotonic

🍀Các chức năng cơ bản
Phần này sẽ hướng dẫn bạn cách tương tác cơ bản với dữ liệu trong Series hoặc DataFrame. Trong các phần sau, chúng ta sẽ đi sâu hơn vào các chủ đề phân tích và thao tác dữ liệu bằng pandas.
🚀 Reindexing
📑 Đúng như cái tên ta có hàm reindex để tạo một object mới với các giá trị được tái sắp xếp bằng bộ index mới
In [98]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=["d", "b", "a", "c"])
In [99]: obj
Out[99]:
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
In [100]: obj2 = obj.reindex(["a", "b", "c", "d", "e"])
In [101]: obj2
Out[101]:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64reindex Series ở trên sẽ trả ra NaN nếu có bất kỳ giá trị index nào không tồn tại
📑 Đối với các dữ liệu có đặc tính trình tự như time series, bạn có thể muốn thực hiện một số phép nội suy (“interpolation”) tính toán hoặc lắp các giá trị bị thiếu khi reindexing.
Ta có thể dùng tham số method trong reindex để sử dụng nhưng cách khác nhau để điền giá trị, ví dụ sau đây dùng ffill để điền giá trị liền trước (“forawrd-fills”)
In [102]: obj3 = pd.Series(["blue", "purple", "yellow"], index=[0, 2, 4])
In [103]: obj3
Out[103]:
0 blue
2 purple
4 yellow
dtype: object
In [104]: obj3.reindex(np.arange(6), method="ffill")
Out[104]:
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: objectTrong DataFrame, reindex có thể thay đổi (row) index, columns hoặc cả hai. Khi truyền vào một sequence, nó reindex các dòng tương ứng
In [105]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
.....: index=["a", "c", "d"],
.....: columns=["Ohio", "Texas", "California"])
In [106]: frame
Out[106]:
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
In [107]: frame2 = frame.reindex(index=["a", "b", "c", "d"])
In [108]: frame2
Out[108]:
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0Khi muốn reindex các cột thì ta dùng keyword columns
In [109]: states = ["Texas", "Utah", "California"]
In [110]: frame.reindex(columns=states)
Out[110]:
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8Kết quả trên có thể thấy do “Ohio” không có trong danh sách states nên cột đó trả về rỗng
Cũng trong ví dụ trên, một cách khác để reindex một trục nhất định (rows hoặc columns) ta có thể dùng tham số axis
In [111]: frame.reindex(states, axis="columns")
Out[111]:
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
reindex function argumentsỞ chức năng về Selection ta sẽ được biết hàm loc cũng có khả năng reindex nhưng nó chỉ chạy được khi các index labels có tồn tại trong DF (khác với reindex sẽ thêm các giá trị rỗng vào.
In [112]: frame.loc[["a", "d", "c"], ["California", "Texas"]]
Out[112]:
California Texas
a 2 1
d 8 7
c 5 4🚀 Dropping Entries from an Axis
việc bỏ một hoặc nhiều mục mặc dù có thể làm bằng hai hàm reindex hoặc .loc nhưng ta cũng có thể dùng hàm drop để chỉ đích dành dòng muốn bỏ
In [113]: obj = pd.Series(np.arange(5.), index=["a", "b", "c", "d", "e"])
In [114]: obj
Out[114]:
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
In [115]: new_obj = obj.drop("c")
In [116]: new_obj
Out[116]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
In [117]: obj.drop(["d", "c"])
Out[117]:
a 0.0
b 1.0
e 4.0
dtype: float64Tất nhiên ta cũng có thể bỏ cả dòng và cột, xem ví dụ sau:
# Create new DataFrame
In [118]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
.....: index=["Ohio", "Colorado", "Utah", "New York"],
.....: columns=["one", "two", "three", "four"])
In [119]: data
Out[119]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
# Drop rows
In [120]: data.drop(index=["Colorado", "Ohio"])
Out[120]:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
# Drop columns
In [121]: data.drop(columns=["two"])
Out[121]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15Còn một cách khác nữa là dùng tham số axis
In [122]: data.drop("two", axis=1) # like NumPy
Out[122]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
In [123]: data.drop(["two", "four"], axis="columns")
Out[123]:
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
New York 12 14🚀Indexing
Cú pháp Indexing của Series (obj[...]) hoạt động tương tự NumPy chỉ khác là ta vừa có thể dùng dạng index labels hoặc index integers.
In [124]: obj = pd.Series(np.arange(4.), index=["a", "b", "c", "d"])
In [125]: obj
Out[125]:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
In [126]: obj["b"]
Out[126]: 1.0
In [127]: obj[1]
Out[127]: 1.0
In [128]: obj[2:4]
Out[128]:
c 2.0
d 3.0
dtype: float64
In [129]: obj[["b", "a", "d"]]
Out[129]:
b 1.0
a 0.0
d 3.0
dtype: float64
In [130]: obj[[1, 3]]
Out[130]:
b 1.0
d 3.0
dtype: float64
In [131]: obj[obj < 2]
Out[131]:
a 0.0
b 1.0
dtype: float64🔥.loc hoặc iloc
Thường thì nếu dùng index labels ta ưu tiên dùng hàm loc hơn và tương tự nếu dùng dạng index integers thì ta dùng hàm iloc
In [132]: obj.loc[["b", "a", "d"]]
Out[132]:
b 1.0
a 0.0
d 3.0
dtype: float64
In [139]: obj1.iloc[[0, 1, 2]]
Out[139]:
2 1
0 2
1 3
dtype: int64
In [140]: obj2.iloc[[0, 1, 2]]
Out[140]:
a 1
b 2
c 3
dtype: int64Nếu dùng lộn thì ta sẽ gặp lỗi này:
In [134]: obj2.loc[[0, 1]]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/tmp/ipykernel_804589/4185657903.py in <module>
----> 1 obj2.loc[[0, 1]]
^ LONG EXCEPTION ABBREVIATED ^
KeyError: "None of [Int64Index([0, 1], dtype="int64")] are in the [index]"💡 Ta có thể slice bằng labels nhưng khác với việc slicing thuần túy trong Python kết quả sẽ bao gồm luôn endpoint
In [141]: obj2.loc["b":"c"]
Out[141]:
b 2
c 3
dtype: int64Như đã đề cập thì việc sửa đổi sẽ ánh xạ lên phần đó của Series
In [142]: obj2.loc["b":"c"] = 5
In [143]: obj2
Out[143]:
a 1
b 5
c 5
dtype: int64Các bạn mới có thể sẽ hay nhầm lần việc gọi
Noteilochaylocgiống như cách gọi hàm thay vì dùng ký hiệu ngoặc vuông. Bản chất việc này giống như cách ta slicing với việc ta có thểindexing intonhiều cột/dòng trong DataFrame.
🔥Single value or Sequence values
In [144]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
.....: index=["Ohio", "Colorado", "Utah", "New York"],
.....: columns=["one", "two", "three", "four"])
In [145]: data
Out[145]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
In [146]: data["two"]
Out[146]:
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int64
In [147]: data[["three", "one"]]
Out[147]:
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12🔥Boolean array
Ta cũng có thể slicing hoặc selecting với mảng Boolean
In [148]: data[:2]
Out[148]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
In [149]: data[data["three"] > 5]
Out[149]:
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15Khi ta làm phép so sánh DataFrame với giá trị đại số ta cũng tạo ra một Boolean DataFrame mới:
In [150]: data < 5
Out[150]:
one two three four
Ohio True True True True
Colorado True False False False
Utah False False False False
New York False False False FalseTận dụng cách trên ta có thể gán 0 cho các giá trị True
In [151]: data[data < 5] = 0
In [152]: data
Out[152]:
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15🚀Selection
Phổ biến nhất ta có thể dùng iloc hoặc loc để lấy dữ liệu mong muốn. Hãy nhớ bản chất DataFrame là cấu trúc 2 chiều
🔥Dùng .loc
In [153]: data
Out[153]:
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
In [154]: data.loc["Colorado"]
Out[154]:
one 0
two 5
three 6
four 7
Name: Colorado, dtype: int64Để chọn nhiều dòng ta truyền vào 1-sequence
In [155]: data.loc[["Colorado", "New York"]]
Out[155]:
one two three four
Colorado 0 5 6 7
New York 12 13 14 15Thú vị là ta có thể kết hợp cả hai chỉ bằng dấu ,
In [156]: data.loc["Colorado", ["two", "three"]]
Out[156]:
two 5
three 6
Name: Colorado, dtype: int64🔥Dùng .iloc
Ta có thể làm việc tương tự với iloc
In [157]: data.iloc[2]
Out[157]:
one 8
two 9
three 10
four 11
Name: Utah, dtype: int64
In [158]: data.iloc[[2, 1]]
Out[158]:
one two three four
Utah 8 9 10 11
Colorado 0 5 6 7
In [159]: data.iloc[2, [3, 0, 1]]
Out[159]:
four 11
one 8
two 9
Name: Utah, dtype: int64
In [160]: data.iloc[[1, 2], [3, 0, 1]]
Out[160]:
four one two
Colorado 7 0 5
Utah 11 8 9🔥Dùng slices
Cả hai hàm indexing để có cơ chế slicing
In [161]: data.loc[:"Utah", "two"]
Out[161]:
Ohio 0
Colorado 5
Utah 9
Name: two, dtype: int64
In [162]: data.iloc[:, :3][data.three > 5]
Out[162]:
one two three
Colorado 0 5 6
Utah 8 9 10
New York 12 13 14🔥Boolean array
Lưu ý là Boolean arrays chỉ dùng cho loc
In [163]: data.loc[data.three >= 2]
Out[163]:
one two three four
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
🚀Lỗi interger indexing
Một vài lỗi thông dụng vì đôi khi ta hay nhầm lẫn với các cấu trúc dữ liệu khác như list hay tuple
In [164]: ser = pd.Series(np.arange(3.))
In [165]: ser
Out[165]:
0 0.0
1 1.0
2 2.0
dtype: float64
In [166]: ser[-1]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
nge.py in get_loc(self, key)
344 try:
--> 345 return self._range.index(new_key)
346 except ValueError as err:
ValueError: -1 is not in range
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
<ipython-input-166-44969a759c20> in <module>
----> 1 ser[-1]
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py
in __getitem__(self, key)
1010
1011 elif key_is_scalar:
-> 1012 return self._get_value(key)
1013
1014 if is_hashable(key):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py
in _get_value(self, label, takeable)
1119
1120 # Similar to Index.get_value, but we do not fall back to position
al
-> 1121 loc = self.index.get_loc(label)
1122
1123 if is_integer(loc):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
nge.py in get_loc(self, key)
345 return self._range.index(new_key)
346 except ValueError as err:
--> 347 raise KeyError(key) from err
348 self._check_indexing_error(key)
349 raise KeyError(key)
KeyError: -1Mặc dù pandas có thể “fall back” khi indexing integer nhưng chỉ đúng khi bạn dùng label indexing
In [168]: ser2 = pd.Series(np.arange(3.), index=["a", "b", "c"])
In [169]: ser2[-1]
Out[169]: 2.0Nhưng nếu bạn dùng iloc hay loc thì sẽ khác
In [170]: ser.iloc[-1]
Out[170]: 2.0Ngoài ra thì khi slicing bằng interger thì có tính chất interger oriented:
In [171]: ser[:2]
Out[171]:
0 0.0
1 1.0
dtype: float64🚀Lỗi với chain indexing
Ngoài việc dùng loc và iloc cho việc selection thì ta còn có thể dùng để modify nhưng cần lưu ý cẩn thận
In [172]: data.loc[:, "one"] = 1
In [173]: data
Out[173]:
one two three four
Ohio 1 0 0 0
Colorado 1 5 6 7
Utah 1 9 10 11
New York 1 13 14 15
In [174]: data.iloc[2] = 5
In [175]: data
Out[175]:
one two three four
Ohio 1 0 0 0
Colorado 1 5 6 7
Utah 5 5 5 5
New York 1 13 14 15
In [176]: data.loc[data["four"] > 5] = 3
In [177]: data
Out[177]:
one two three four
Ohio 1 0 0 0
Colorado 3 3 3 3
Utah 5 5 5 5
New York 3 3 3 3Một sai lầm phổ biến đối với người dùng pandas mới là liên kết selections khi gán, như thế này:
In [177]: data.loc[data.three == 5]["three"] = 6
<ipython-input-11-0ed1cf2155d5>:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value insteadCú pháp trên khá là quen khi học NumPy nhỉ nhưng ở đây không áp dụng được
Tùy thuộc vào nội dung dữ liệu, điều này có thể in ra một cảnh báo đặc biệt SettingWithCopyWarning, cảnh báo bạn rằng bạn đang cố gắng sửa đổi một giá trị tạm thời (kết quả nonempty của data.loc[data.three == 5]) thay vì data ban đầu. Như vậy, DataFrame data không được sửa đổi:
In [179]: data
Out[179]:
one two three four
Ohio 1 0 0 0
Colorado 3 3 3 3
Utah 5 5 5 5
New York 3 3 3 3Còn nếu muốn sửa thì ta cần viết như sau:
In [180]: data.loc[data.three == 5, "three"] = 6
In [181]: data
Out[181]:
one two three four
Ohio 1 0 0 0
Colorado 3 3 3 3
Utah 5 5 6 5
New York 3 3 3 3🍀Kết luận
Bài hôm nay chúng ta cũng hình dung được DataFrame như thế nào và làm việc với các chức năng cơ bản nhất là Indexing và Selection. Ở bài sau ta sẽ tìm hiểu thêm về các chức năng tính toán, viết hàm ufuncs, sắp xếp và xếp hạng trong DF. Sau đó thử áp dụng một vài hàm thống kê cơ bản đảm bảo sẽ thú vị hơn
Các kênh cộng đồng của mình
- Facebook: https://www.facebook.com/bocuoilamdata
- Youtube: https://www.youtube.com/@CarrY4U_VN
- Website: https://carry4u.org/
© Bản quyền thuộc về CarrY4U và Bò Cười Làm Data
© Copyright by CarrY4U and bocuoilamdata ☞ Do not reup

