Hướng dẫn dùng python itemgetter python

Tôi có một danh sách các danh sách chứa các số được đại diện chuỗi:

nums = [['1','3'],['2','2'],['1','2'],['0','2'],['11','2']]

Tôi cần sắp xếp các số này tăng dần theo các mục đầu tiên rồi đến mục thứ hai mà không cần sửa đổi biểu diễn chuỗi của các số trong danh sách ban đầu. Ngoài ra, muốn tránh tạo một bản sao thứ hai khác của danh sách với mọi thứ được ánh xạ thành số nguyên một cách rõ ràng - hãy tưởng tượng đây là một danh sách khổng lồ.

Cả hai sort()và đều sorted()hoạt động tốt với các bộ dữ liệu và danh sách, vì vậy với khóa lambda, tôi có thể làm như sau:

>>> sorted(nums, key=lambda n: (int(n[0]),int(n[1])) 
[['0', '2'], ['1', '2'], ['1', '3'], ['2', '2'], ['11', '2']]

Những ngày hạnh phúc...

Tuy nhiên, tôi đã thấy một số bài đăng về việc sắp xếp nhanh hơn bằng cách sử dụng hàm operator.itemgetter()as key so với việc sử dụng lambda. Không cần thảo luận về tính hợp lệ của những tuyên bố này , có ai nếu có thể áp dụng chuyển đổi từ chuỗi sang số nguyên để so sánh khi sử dụng operator.itemgetter():

Điều sau rõ ràng không thành công, do các chuỗi được so sánh dưới dạng chuỗi, không phải số:

>>> sorted(nums, key=operator.itemgetter(0,1)) 
[['0', '2'], ['1', '2'], ['1', '3'], ['11', '2'], ['2', '2']]

4 hữu ích 5 bình luận 1.8k xem chia sẻ

Trong bài trước, chúng ta đã tìm hiểu một vài khái niệm và thao tác toán học cơ bản trong thuật toán kNN đơn giản là tìm khoảng cách ngắn nhất từ k điểm đến một điểm bất kỳ. Toàn bộ nội dung của thuật toán này được tham khảo từ chương trình chương 2, mục 2.1, cuốn sách Machine Learning in Action như sau:

from numpy import *

import operator

import matplotlib.pyplot as plt

def createDataSet():

  group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])

  labels = ['A','B','C','D']

  return group, labels

def classify0(inX, dataSet, labels, k):

  dataSetSize = dataSet.shape[0]

  diffMat = tile(inX, (dataSetSize,1)) - dataSet

  sqDiffMat = diffMat**2

  sqDistances = sqDiffMat.sum(axis=1)

  distances = sqDistances**0.5

  sortedDistIndicies = distances.argsort()

  classCount={}

  for i in range(k):

    voteIlabel = labels[sortedDistIndicies[i]]

    classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1

    sortedClassCount = sorted(classCount.items(),

                key=operator.itemgetter(1), reverse=True)

  return sortedClassCount[0][0]

Nếu đọc kỹ bài trước (https://ngocminhtran.com/2020/02/16/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn/), chúng ta có thể hiểu được mục đích tính distances từ hàm classify0 (đây là phiên bản đơn giản và chúng ta sẽ tìm hiểu các phiên bản classify khác trong các bài sau). Bài viết này sẽ tiếp tục tìm hiểu Python thông qua phần còn lại của hàm classify0.

Khi kết thúc bài trước, chúng ta đã tính được vec tơ khoảng cách (Khoangcach):

Hướng dẫn dùng python itemgetter python

Đây chính là distances trong hàm classify0. Kết quả cụ thể của distanceslà:

distances = Khoangcach = [ 1.48660687   1.41421356     0.          0.1  ]

Với vec tơ distances chúng ta đã xác định được các khoảng cách OM, ON, OP và OK và có thể biết được khoảng cách gần nhất là OP. Nhưng làm thế nào để chương trình biết điều đó đồng thời thuật toán kNN cũng phải chỉ ra lớp (hay label) của điểm (hay tập điểm) có khoảng cách gần điểm O nhất? Hiển nhiên là chúng ta có thể dự đoán là điểm P và lớp B.

Hàm argsort()

Mô đun NumPy của Python hỗ trợ hàm argsort() trả về vec tơ chỉ số được sắp xếp theo thứ tự tăng dần (theo giá trị tại chỉ số đó) từ một vec tơ cho trước với chú ý rằng các chỉ số bắt đầu từ 0. Xét đoạn mã sau từ hàm classify0:

sortedDistIndicies = distances.argsort()

sortedDistIndicies là vec tơ chỉ số được sắp tăng dần (theo gia trị) từ distances:

sortedDistIndicies = [2 3 1 0]

Ở đây 2 là chỉ số đầu tiên vì trong vec tơ distances giá trị tại chỉ số 2 là 0 là bé nhất, kế tiếp là 0.1 nên chỉ số là 3 và cứ thế.

Cấu trúc dictionary

Chúng ta để ý khai báo trong hàm classify0:

classCount={}

biến classCount có kiểu dữ liệu mà trong Python gọi là kiểu dictionary. Các phần tử trong một dictionary được có dạng cặp key:value hay rỗng (như khai báo biến classCount ở trên) như ví dụ sau:

dic = {“A”:3, “B”:2}

Ở đây, A và B là các key (hay khóa) và 3 và 2 là các value (hay giá trị tương ứng với key). Muốn biết giá trị của các key, chúng ta chỉ việc truy cập đến key đó như ví dụ:

print(dic['A']) # 3

print(dic['B']) # 2

Có hai chú ý:

  • Trong hai đoạn mã trên, tôi đã dùng ký hiệu # phía sau hai lệnh print. Trong Python, dấu # dùng để chú thích giống dấu // trong các ngôn ngữ họ C như C, C++, Java,…
  • Truy cập đến giá trị của một key nào đó dùng cặp ngoặc vuông kèm theo tên của key.

Tuy nhiên, nếu chúng ta truy cập đến giá trị của một key theo cách trên có thể dẫn tới lỗi nếu key không tồn tại trong dic. Ví dụ chúng ta gõ lệnh:

print(dic['C']) # phát sinh lỗi

Để giải quyết vấn đề này, Python cung cấp hàm get() như sau:

print(dic.get('C', 'Khoa khong ton tai!')) # Khoa khong ton tai

Tham số đầu tiên của get chính là key và tham số thứ hai là giá trị trả về nếu key đó không tồn tại trong dictionary (thay vì phát sinh lỗi).

Lúc này, chúng ta có thể hiểu được dòng lệnh:

classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1

hàm getbên phải sẽ trả về 0 nếu key voteIlabel không tồn tại trong classCount. Chúng ta phải cộng thêm 1 vì classCountban đầu được khởi tạo là rỗng ({}).

Bên cạnh get(), Python còn hỗ trợ cho kiểu dictionary một hàm khác cũng phổ biến không kém là hàm items() dùng để trả về một danh sách (list) tất cả các cặp key/value của một dictionary. Ví dụ:

print(dic.items()) # dict_items([('A', 3), ('B', 2)])

Hàm sorted()

Việc sử dụng hàm items() trong dictionary sẽ hiệu quả hơn nếu được kết hợp với hàm sortednhư đoạn lệnh kế tiếp trong classify0:

sortedClassCount = sorted(classCount.items(),

                    key=operator.itemgetter(1), reverse=True)

Một số chú ý:

  • Tham số đầu tiên của sortedchính là danh sách các cặp key/value từ dictionary (nhận được nhờ items()).
  • Tham số thứ hai là khóa mà chúng ta dựa vào đó để sắp xếp (lưu ý, key này không liên quan gì đến key/value trong dictionary) bằng cách dùng hàm opearator.itemgetter().Hàm operator.itemgetter()nhận một trong hai giá trị là 0 hay 1. Nếu giá trị nhận là 0 tức là chúng ta sẽ sắp xếp danh sách (tham số đầu tiên) dựa trên key từ các cặp key/value trong dictionary; ngược lại, nếu nhận là 1 thì dựa trên value.
  • Tham số cuối cùng quy định sắp xếp giảm dần nếu reverse = True, ngược lại sắp tăng dần. Mặc định reverse = False.

Xem xét kết quả từ đoạn mã sau:

dicSorted = sorted(dic.items(),key=operator.itemgetter(0), 
        
                     reverse=False)

print(dicSorted)

Kết quả là danh sách xếp tăng dần theo key:

[(‘A’, 3), (‘B’, 2)]

Thay đổi một ít để sắp tăng dần theo value:

dicSorted = sorted(dic.items(),key=operator.itemgetter(1), 
 
                 reverse=False)

print(dicSorted)

Kết quả:

[(‘B’, 2), (‘A’, 3)]

Các cặp trong danh sách trả về từ hàm sortedcó thể được truy cập theo chỉ số, ví dụ truy cập đến cặp đầu tiên (sau khi được sắp) trong dicSorted:

dicSorted = sorted(dic.items(),key=operator.itemgetter(1), 

                   reverse=False)

print(dicSorted[0]) # ('B', 2)

Muốn hiển thị key của cặp thứ nhất:

print(dicSorted[0][0]) # B

Muốn hiển thị value của cặp thứ nhất:

print(dicSorted[0][1]) # 2

Cho đến lúc này chúng ta đã tìm hiểu chi tiết về cấu trúc dictionary và các hàm sắp xếp trong Python, trước khi xem lại toàn bộ hàm classify0 chúng ta sẽ xem xét cấu trúc lặp trong Python.

Cấu trúc lặp

Giống như hầu hết các ngôn ngữ lập trình, Python cung cấp hai lệnh lặp cơ bản là whilefor. Nói về whilefor trong Python có thể tìm thấy vô số tài liệu và ví dụ trên Internet và trong bài viết này sẽ không đề cập chi tiết vì mục đích của loạt bài này là chỉ bàn về các đặc trưng của Python trong khuôn khổ chương trình đang xét.

Trong hàm classify0chúng ta bắt gặp lệnh fornhư sau:

for i in range(k):

Lệnh này thực thi các giá trị i =0, 1, 2,…,k-1 và kết thúc for (hay while) sẽ là dấu hai chấm. Ví dụ hiển thị các giá trị từ 0 đến 9, lệnh:

for i in range(10):

print(i)

Như vậy, chúng ta đã khám phá phần còn lại của thuật toán kNN tính khoảng cách ngắn nhất với các đặc trưng thú vị trong Python như dictionary, hàm get, hàm argsort, hàm sorted, for. Trong các bài viết kế tiếp chúng ta sẽ tìm hiểu các phiên bản phức tạp hơn của thuật toán kNN và qua đó cũng khám phá thêm nhiều đặc trưng thú vị khác trong Python.