Lập trình với R: lapply and sapply

lapply and sapply

lapply and sapply

Trong bài viết này, ta sẽ học cách sử dụng hàm lapply() và sapply(), hai trong số những hàm quan trọng trong R. Chúng được gọi là loop function trong nhóm hàm *apply(). Những hàm mạnh mẽ này, cùng với các hàm gần kề chúng (vapply() và tapply()) cung cấp các công cụ chính xác và tiện lợi trong chiến lược phân tích dữ liệu Split-Apply-Combine (tách-áp dụng-kết hợp). Những hàm *apply này sẽ TÁCH (SPLIT) dữ liệu ban đầu ra thành những thành phần nhỏ hơn, ÁP DỤNG (APPLY) một tác vụ nào đó lên các thành phần này, và KẾT HỢP (COMBINE) các kết quả này lại. Để tìm hiểu chi tiết hơn về chiến lược này ta có thể tham khảo trong bài báo của Hadley Wickham’s Journal of Statistical Software có tiêu đề là “The Split-Apply-Combine Strategy for Data Analysis”.

Trong suốt bài viết này, ta sẽ sử dụng tập dữ liệu Flags (quốc kỳ) từ UCI Machine Learning Repository. Tập dữ liệu này chứa thông tin chi tiết về quốc kỳ của các quốc gia. Để biết thêm thông tin hãy truy cập vào link này: http://archive.ics.uci.edu/ml/datasets/Flags.

lapply

Chúng ta hãy xem những hàm này đặc biệt như thế nào nhé. Sau khi nạp dữ liệu vào biến flags. Chúng ta hãy quan sát tổng quan dữ liệu.

flags <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/flags/flag.data"))

# liệt kê 6 dòng dữ liệu ban đầu
head(flags)
head(flags)
            name landmass zone area population language religion bars stripes
1    Afghanistan        5    1  648         16       10        2    0       3
2        Albania        3    1   29          3        6        6    0       0
3        Algeria        4    1 2388         20        8        2    2       0
4 American-Samoa        6    3    0          0        1        1    0       0
5        Andorra        3    1    0          0        6        0    3       0
6         Angola        4    2 1247          7       10        5    0       2
  colours red green blue gold white black orange mainhue circles crosses
1       5   1     1    0    1     1     1      0   green       0       0
2       3   1     0    0    1     0     1      0     red       0       0
3       3   1     1    0    0     1     0      0   green       0       0
4       5   1     0    1    1     1     0      1    blue       0       0
5       3   1     0    1    1     0     0      0    gold       0       0
6       3   1     0    0    1     0     1      0     red       0       0
  saltires quarters sunstars crescent triangle icon animate text topleft
1        0        0        1        0        0    1       0    0   black
2        0        0        1        0        0    0       1    0     red
3        0        0        1        1        0    0       0    0   green
4        0        0        0        0        1    1       1    0    blue
5        0        0        0        0        0    0       0    0    blue
6        0        0        1        0        0    1       0    0     red
  botright
1    green
2      red
3    white
4      red
5      red
6    black

# kiểm tra số chiều của dữ liệu
dim(flags)
[1] 194  30

Quan sát trên cho ta biết tập dữ liệu có 194 dòng và 30 cột thuộc tính. Mỗi dòng thể hiện một quốc gia và mỗi cột thể hiện các đặc trưng quốc kỳ của từng quốc gia đó.

Như bất kỳ tập dữ liệu nào, ta muốn biết định dạng lưu trữ của các thuộc tính. Hay nói cách khác, các thuộc tính thuộc kiểu dữ liệu ‘class’ nào. Điều gì sẽ xảy ra nếu ta nhập lệnh class(flags).

class(flags)
[1] "data.frame"

Kết quả trên cho ta thấy, toàn bộ dữ liệu được lưu dưới kiểu dữ liệu ‘data.frame’, không phải là kết quả chúng ta cần tìm. Cái chúng ta cần đó là hàm class() sẽ áp dụng lên từng cột dữ liệu. Chúng ta có thể làm lần lượt cho từng cột dữ liệu. Nhưng có một cách nhanh hơn đó là dùng vòng lặp (loop).

Hàm lapply() nhận đối số đầu vào là một list, áp dụng tính toán trên từng phần tử của list này, sau đó trả về một list khác có cùng độ dài với list ban đầu. Được biết data frame cũng là một list gồm các vectors (bạn có thể kiểm chứng bằng lệnh as.list(flags)), chúng ta có thể sử dụng lapply() để áp dụng hàm class() lên từng cột dữ liệu của tập dữ liệu flags.

cls_list <- lapply(flags, class)
cls_list
$name
[1] "factor"

$landmass
[1] "integer"

$zone
[1] "integer"

$area
[1] "integer"

$population
[1] "integer"

$language
[1] "integer"

$religion
[1] "integer"

$bars
[1] "integer"

$stripes
[1] "integer"

$colours
[1] "integer"

$red
[1] "integer"

$green
[1] "integer"

$blue
[1] "integer"

$gold
[1] "integer"

$white
[1] "integer"

$black
[1] "integer"

$orange
[1] "integer"

$mainhue
[1] "factor"

$circles
[1] "integer"

$crosses
[1] "integer"

$saltires
[1] "integer"

$quarters
[1] "integer"

$sunstars
[1] "integer"

$crescent
[1] "integer"

$triangle
[1] "integer"

$icon
[1] "integer"

$animate
[1] "integer"

$text
[1] "integer"

$topleft
[1] "factor"

$botright
[1] "factor"

# kiểm tra kiểu dữ liệu của cls_list
class(cls_list)
[1] "list"

Kí tự ‘l’ trong ‘lapply’ là viết tắt của ‘list’. Khi kiểm tra class(cls_list) ta sẽ thấy điều này. Như dự đoán, ta có một list với độ dài là 30 bằng với số lượng của các cột thuộc tính. Kết quả trả ra sẽ gọn gàng hơn nếu chúng ta có thể biểu diễn nó dưới dạng một vector thay vì một list. Như các bài viết trước đã đề cập, công dụng của kiểu dữ liệu list đó là nó có thể lưu nhiều kiểu dữ liệu cùng lúc bên trong nó. Trong trường hợp này, khi các phần tử của list trả về bởi hàm lapply() là các kiểu dữ liệu character với độ dài là một, cls_list có thể được tối giản thành vector chứa các phần tử có kiểu dữ liệu là character. Đơn giản chúng ta chỉ cần nhập lệnh as.character(cls_list).

as.character(cls_list)
 [1] "factor"  "integer" "integer" "integer" "integer" "integer" "integer"
 [8] "integer" "integer" "integer" "integer" "integer" "integer" "integer"
[15] "integer" "integer" "integer" "factor"  "integer" "integer" "integer"
[22] "integer" "integer" "integer" "integer" "integer" "integer" "integer"
[29] "factor"  "factor"

sapply

sapply() sẽ tự động thực hiện thao tác trên bằng cách gọi ngầm hàm lapply(), sau đó cố gắng đơn giản hoá kết quả trả về cho bạn.

cls_vect <- sapply(flags, class)
cls_vect
      name   landmass       zone       area population   language   religion
  "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
      bars    stripes    colours        red      green       blue       gold
 "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
     white      black     orange    mainhue    circles    crosses   saltires
 "integer"  "integer"  "integer"   "factor"  "integer"  "integer"  "integer"
  quarters   sunstars   crescent   triangle       icon    animate       text
 "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"
   topleft   botright
  "factor"   "factor"

# kiểm chứng kiểu dữ liệu trả về của sapply là vector chứa các character
class(cls_vect)
[1] "character"

Về mặt tổng quát, nếu kết quả trả về là một list trong đó các phần tử đều có độ dài là một, thì sapply() sẽ trả về một vector. Nếu kết quả trả về là một list mà các phần tử là một vector có cùng độ dài (> 1), sapply() sẽ trả về một ma trận. Nếu sapply() không thể đơn giản hoá, nó chỉ trả về một list, và không có sự khác biệt với hàm lapply() ban đầu.

Dưới đây là tổng hợp các thao tác trên tập dữ liệu flags có sử dụng hàm lapply() và sapply().

# tính tổng các quốc gia có quốc kì chứa màu cam
sum(flags$orange)
[1] 26

# trích chọn các cột dữ liệu thể hiện màu sắc của quốc kì
flag_colors <- flags[, 11:17]

# quan sát 6 dòng đầu tiên của flag_colors
head(flag_colors)
  red green blue gold white black orange
1   1     1    0    1     1     1      0
2   1     0    0    1     0     1      0
3   1     1    0    0     1     0      0
4   1     0    1    1     1     0      1
5   1     0    1    1     0     0      0
6   1     0    0    1     0     1      0

# tính tổng cho từng giá trị màu sắc
lapply(flag_colors, sum)
$red
[1] 153

$green
[1] 91

$blue
[1] 99

$gold
[1] 91

$white
[1] 146

$black
[1] 52

$orange
[1] 26

# tương tự tính toán trên với sapply() để giản lược thành một vector
sapply(flag_colors, sum)
red  green   blue   gold  white  black orange
153     91     99     91    146     52     26 

# trích chọn các cột dữ liệu thể hiện hình dạng của quốc kì
flag_shapes <- flags[, 19:23]

# xác định khoảng giao động giá trị của các hình dạng flag_shapes
lapply(flag_shapes, range)
$circles
[1] 0 4

$crosses
[1] 0 2

$saltires
[1] 0 1

$quarters
[1] 0 4

$sunstars
[1]  0 50

# tương tự tính toán trên với sapply() để giản lược thành một ma trận
shape_mat <- sapply(flag_shapes, range)
shape_mat
shape_mat
     circles crosses saltires quarters sunstars
[1,]       0       0        0        0        0
[2,]       4       2        1        4       50

# kiểm tra kiểu dữ liệu của shape_mat
class(shape_mat)
[1] "matrix"

# xác định giá trị đơn nhất của các cột dữ liệu trong tập dữ liệu flags
unique_vals <- lapply(flags, unique)

# xác định độ dài của dữ liệu unique_vals thông qua hàm sapply()
sapply(unique_vals, length)
sapply(unique_vals, length)
      name   landmass       zone       area population   language   religion
       194          6          4        136         48         10          8
      bars    stripes    colours        red      green       blue       gold
         5         12          8          2          2          2          2
     white      black     orange    mainhue    circles    crosses   saltires
         2          2          2          8          4          3          2
  quarters   sunstars   crescent   triangle       icon    animate       text
         3         14          2          2          2          2          2
   topleft   botright
         7          8

# nhúng hàm trích chọn phần từ thứ hai trong vector vào hàm lapply()
lapply(unique_vals, function(elem) elem[2])

Qua bài viết này, chúng ta đã học được cách sử dụng hai hàm lapply() và sapply() để áp dụng các phép toán lặp lên các phần tử của list.

Nguồn tham khảo: http://swirlstats.com/

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s