2. Data Selection và Indexing trong DataFrame
Cũng giống như Series và đã nhắc ở bài trước, với DataFrame thì ta sẽ hình dung nó như là một mảng 2 chiều hoặc như structured array trong NumPy.
Ngoài ra, ta có thể xem DataFrame giống như là một dictionary chứa các series có chung index với nhau (khá là giống với excel). Ta sẽ tìm hiểu từng trường hợp cụ thể sau:
a. DataFrame là Dictionary
Quay lại với ví dụ ở bài 2, ta có một DataFrame chứa các Series là dữ liệu dân số và diện tích của một số tỉnh / thành phố ở Việt Nam sau:
population = pd.Series({'TP.HCM': 8993, 'Hanoi': 8053, 'Lam Dong': 1297, 'Quang Tri': 623})
area = pd.Series({'TP.HCM': 2061, 'Hanoi': 3359, 'Lam Dong': 9765, 'Quang Tri': 4746})
data = pd.DataFrame({'Dân số': population, 'Diện tích': area})
data
Kết quả :
Dân số Diện tích
TP.HCM 8993 2061
Hanoi 8053 3359
Lam Dong 1297 9765
Quang Tri 623 4746
Từng Series trong DataFrame có thể được truy cập theo kiểu dictionary bằng cách index vào tên của cột tương ứng:
data['Dân số']
TP.HCM 8993
Hanoi 8053
Lam Dong 1297
Quang Tri 623
Name: Dân số, dtype: int64
Ngoài cách trên, pandas cho phép ta truy cập trực tiếp vào các Series như là một thuộc tính của object:
data['population'] = data['Dân số']
data.population
TP.HCM 8993
Hanoi 8053
Lam Dong 1297
Quang Tri 623
Name: population, dtype: int64
Và ta cũng có thể thêm các cặp dữ liệu mới bằng cách tương tự như Series:
data['Mật độ'] = data['Dân số'] / data['Diện tích']
data
Kết quả :
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1297 9765 0.132821
Quang Tri 623 4746 0.131268
2. DataFrame là mảng 2 chiều
Với cách hình dung DataFrame là mảng 2 chiều thì cũng giống như Series là mảng 1 chiều, ta sẽ có một mảng 2 chiều hoạt động khá tương tự với NumPy. Chẳng hạn muốn lấy dữ liệu thô thì ta có thể truy cập qua thuộc tính values:
data.values
array([[8.99300000e+03, 2.06100000e+03, 4.36341582e+00],
[8.05300000e+03, 3.35900000e+03, 2.39743971e+00],
[1.29700000e+03, 9.76500000e+03, 1.32821301e-01],
[6.23000000e+02, 4.74600000e+03, 1.31268437e-01]])
Nếu muốn lấy mảng chuyển vị (hoán đổi cột và hàng), ta có thể truy cập vào thuộc tính T (viết tắt cho transpose):
data.T
TP.HCM Hanoi Lam Dong Quang Tri
Dân số 8993 8053 1297 623
Diện tích 2061 3359 9765 4746
Như vậy ta có thể thấy rằng sẽ có 2 cách để lấy mảng dữ liệu (dạng NumPy) từ một cột trong Pandas như sau:
print("Sử dụng chuyển vị: ", data.T.values[0])
print("Sử dụng index: ", data['Dân số'].values)
Sử dụng chuyển vị: [8993. 8053. 1297. 623.]
Sử dụng index: [8993 8053 1297 623]
c. Indexing trong DataFrame
Giống với Series, ta sẽ sử dụng 2 thuộc tính là loc và iloc. Với iloc, ta sẽ thao tác không khác gì một mảng 2 chiều trong NumPy với các hàng và cột tương ứng vậy, chẳng hạn:
print(data)
# Lấy hàng đầu tiên
print("\nDữ liệu của TP.HCM:\n ",data.iloc[0])
# Lấy dữ liệu 3 hàng đầu tiên
print("\nDữ liệu của TP.HCM, Hà Nội và Lâm Đồng:\n ",data.iloc[:3])
# Lấy dữ liệu 2 hàng và 2 cột đầu tiên
print("\nDữ liệu dân số và diện tích của TP.HCM và Hà Nội:\n ",data.iloc[:2, :2])
# Lấy dữ liệu tất cả các hàng và cột cuối cùng
print("\nMật độ dân số của các tỉnh / thành phố:\n ",data.iloc[:, -1])
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1297 9765 0.132821
Quang Tri 623 4746 0.131268
Dữ liệu của TP.HCM:
Dân số 8993.000000
Diện tích 2061.000000
Mật độ 4.363416
Name: TP.HCM, dtype: float64
Dữ liệu của TP.HCM, Hà Nội và Lâm Đồng:
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1297 9765 0.132821
Dữ liệu dân số và diện tích của TP.HCM và Hà Nội:
Dân số Diện tích
TP.HCM 8993 2061
Hanoi 8053 3359
Mật độ dân số của các tỉnh / thành phố:
TP.HCM 4.363416
Hanoi 2.397440
Lam Dong 0.132821
Quang Tri 0.131268
Name: Mật độ, dtype: float64
Với thuộc tính loc thì ta cũng sử dụng tương tự, nhưng thay vì với implicit index thì ta sử dụng với explicit index tương ứng:
print(data)
# Lấy hàng đầu tiên
print("\nDữ liệu của Quảng Trị:\n ",data.loc['Quang Tri'])
# Lấy dữ liệu 3 hàng đầu tiên
print("\nDữ liệu của TP.HCM, Hà Nội và Lâm Đồng:\n ",data.loc[:'Lam Dong'])
# Lấy dữ liệu 2 hàng và 2 cột đầu tiên
print("\nDữ liệu dân số và diện tích của TP.HCM và Hà Nội:\n ",data.loc[:'Hanoi', :'Diện tích'])
# Lấy dữ liệu tất cả các hàng và cột cuối cùng
print("\nMật độ dân số của các tỉnh / thành phố:\n ",data.loc[:, 'Mật độ'])
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1297 9765 0.132821
Quang Tri 623 4746 0.131268
Dữ liệu của Quảng Trị:
Dân số 623.000000
Diện tích 4746.000000
Mật độ 0.131268
Name: Quang Tri, dtype: float64
Dữ liệu của TP.HCM, Hà Nội và Lâm Đồng:
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1297 9765 0.132821
Dữ liệu dân số và diện tích của TP.HCM và Hà Nội:
Dân số Diện tích
TP.HCM 8993 2061
Hanoi 8053 3359
Mật độ dân số của các tỉnh / thành phố:
TP.HCM 4.363416
Hanoi 2.397440
Lam Dong 0.132821
Quang Tri 0.131268
Name: Mật độ, dtype: float64
Ta có thể kết hợp hai tính năng quan trọng trong NumPy là Masks và Fancy Indexing để tạo ra những câu truy vấn phức tạp hơn, ví dụ như:
print("\nDân số và mật độ của các tỉnh / TP có diện tích > 4000km2:\n", data.loc[data['Diện tích'] > 4000, ['Dân số', 'Mật độ']])
print("\nDiện tích của các tỉnh / TP có mật độ < 1000 người/km2 và dân số < 1 triệu người:\n", data.loc[(data['Mật độ'] < 1) & (data['Dân số'] < 1000), ['Dân số', 'Mật độ']])
Dân số và mật độ của các tỉnh / TP có diện tích > 4000km2:
Dân số Mật độ
Lam Dong 1297 0.132821
Quang Tri 623 0.131268
Diện tích của các tỉnh / TP có mật độ < 1000 người/km2 và dân số < 1 triệu người:
Dân số Mật độ
Quang Tri 623 0.131268
Và ta cũng có thể sử dụng bất kỳ thuộc tính nào trong 2 thuộc tính trên để thay đổi giá trị trong DataFrame:
# Thay đổi dữ liệu mật độ dân số của tỉnh Quảng Trị bằng iloc
data.iloc[3, 2] = 1312
print("\nDữ liệu tỉnh Quảng Trị được cập nhật: \n", data)
# Thay đổi dữ liệu dân số của tỉnh Lâm Đồng bằng loc
data.loc['Lam Dong', 'Dân số'] = 1312
print("\nDữ liệu tỉnh Lâm Đồng được cập nhật: \n", data)
Dữ liệu tỉnh Quảng Trị được cập nhật:
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1312 9765 0.132821
Quang Tri 623 4746 1312.000000
Dữ liệu tỉnh Lâm Đồng được cập nhật:
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1312 9765 0.132821
Quang Tri 623 4746 1312.000000
Có một vài quy ước trong Pandas không hoàn toàn giống NumPy và ta nên nắm rõ vì nó khá hữu dụng trong thực tế. Đầu tiên đó là indexing thường chỉ đến cột, còn slicing thường dành cho hàng:
# Indexing
print("Indexing: \n", data['Dân số'])
# Slicing
print("\nSlicing: \n",data['Hanoi':'Quang Tri'])
Indexing:
TP.HCM 8993
Hanoi 8053
Lam Dong 1312
Quang Tri 623
Name: Dân số, dtype: int64
Slicing:
Dân số Diện tích Mật độ
Hanoi 8053 3359 2.397440
Lam Dong 1312 9765 0.132821
Quang Tri 623 4746 1312.000000
Tiếp theo, nếu như dùng slicing thì pandas sẽ mặc định tham chiếu đến implicit index:
print(data[0:2])
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Và cuối cùng, tương tự slicing thì masking sẽ tham chiếu đến hàng tương ứng thay vì cột:
print(data[data['Dân số'] > 1000])
Dân số Diện tích Mật độ
TP.HCM 8993 2061 4.363416
Hanoi 8053 3359 2.397440
Lam Dong 1312 9765 0.132821