Chuyển đến nội dung chính

Groupby, split-apply-combine và pandas

Trong hướng dẫn này, bạn sẽ học cách dùng phép groupby của pandas, dựa trên chiến lược split-apply-combine nổi tiếng, trên dữ liệu phim Netflix.
· 8 phút đọc

Khám phá cùng AI

Gần đây Netflix đã công bố một số dữ liệu đánh giá của người dùng. Tôi muốn đặt một câu hỏi đơn giản: người đăng ký Netflix thích phim cũ hay mới hơn?

Về mặt trực giác, bạn sẽ muốn chia bộ dữ liệu thành các nhóm theo từng năm, rồi tính một thống kê tóm tắt như trung bình hoặc trung vị, sau đó xem liệu thống kê này có tăng theo các năm hay không (sau đó, bạn có thể muốn thực hiện một phép kiểm định thống kê).

Điều tuyệt vời là có một khung khái niệm để làm và suy nghĩ về điều này, cùng với các triển khai cả trong Python và R. Khung đó được gọi là "split-apply-combine" vì chúng ta...

  • Bước 1: chia dữ liệu thành các nhóm bằng cách tạo một đối tượng groupby từ DataFrame gốc;
  • Bước 2: áp dụng một hàm, trong trường hợp này là hàm tổng hợp để tính thống kê tóm tắt (bạn cũng có thể biến đổi hoặc lọc dữ liệu ở bước này);
  • Bước 3: kết hợp kết quả thành một DataFrame mới.

Đây là khung khái niệm cho phân tích đang xét. Trong bài viết này, bạn sẽ học cách thực hiện để trả lời câu hỏi về đánh giá phim Netflix ở trên bằng gói Python pandas. Bạn cũng có thể làm tương tự trong R, ví dụ bằng gói dplyr. Tôi cũng sẽ đi sâu vào các đối tượng groupby, vốn không trực quan nhất. Quy trình split-apply-combine với các đối tượng groupby là một mẫu hình mà tất cả chúng ta đều thực hiện một cách trực giác, như sẽ thấy, nhưng phải đến năm 2011 Hadley Wickham mới hệ thống hóa quy trình này trong bài báo The Split-Apply-Combine Strategy for Data Analysis.

Nếu bạn thấy kỹ thuật này hữu ích, bạn có thể tìm hiểu thêm (cùng nhiều nội dung khác) và thực hành trong khóa Manipulating DataFrames with pandas của chúng tôi.

Khám phá dữ liệu với pandas

Nhập dữ liệu của bạn

Tại đây bạn sẽ sử dụng pandas, các đối tượng groupby và nguyên tắc split-apply-combine để xem phạm vi điểm phim trên Netflix thay đổi theo năm phát hành như thế nào. Tôi ban đầu tìm thấy dữ liệu trên data.world tại đây. Bạn có thể tìm toàn bộ mã trong bài viết này tại đây nếu muốn tái hiện.

Trước hết bạn sẽ nhập các gói cần thiết và dữ liệu, rồi xem 5 hàng đầu tiên:

# Import packages and set visualization style
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
%matplotlib inline
# Import data and check out head of DataFrame
df = pd.read_csv('data/chasewillden-netflix-shows/data/netflix.csv')
df.head()
 titleratingratinglevelratingdescriptionrelease_yearuser_rating_scoreuser_rating_size
0White ChicksPG-13crude and sexual humor, language and some drug...80200482.080
1Lucky Number SlevinRstrong violence, sexual content and adult lang...1002006NaN82
2Grey's AnatomyTV-14Parents strongly cautioned. May be unsuitable ...90201698.080
3Prison BreakTV-14Parents strongly cautioned. May be unsuitable ...90200898.080
4How I Met Your MotherTV-PGParental guidance suggested. May not be suitab...70201494.080

Trông khá ổn: bạn có tiêu đề, phân loại độ tuổi, năm phát hành và điểm người dùng, cùng một số cột khác. Trước khi thực hiện quy trình groupby và split-apply-combine, hãy xem kỹ dữ liệu hơn một chút để đảm bảo đúng như ta nghĩ và xử lý giá trị khuyết. Lưu ý có một giá trị khuyết NaN ở cột user_rating_score của hàng thứ hai (hàng 1).

Tóm tắt dữ liệu bằng biểu đồ và thống kê

Phương thức .info() của DataFrame pandas vô cùng hữu ích. Áp dụng bên dưới cho thấy bạn có 1000 hàng và 7 cột dữ liệu, nhưng cột quan tâm, user_rating_score, chỉ có 605 giá trị khác null. Điều này có nghĩa là có 395 giá trị khuyết:

# Check out info of DataFrame
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 7 columns):
title                1000 non-null object
rating               1000 non-null object
ratinglevel          941 non-null object
ratingdescription    1000 non-null int64
release_year         1000 non-null int64
user_rating_score    605 non-null float64
user_rating_size     1000 non-null int64
dtypes: float64(1), int64(3), object(3)
memory usage: 54.8+ KB

Bạn có thể loại các hàng có giá trị khuyết bất kỳ, loại các hàng trùng lặp và vẽ pairplot của DataFrame bằng seaborn để có cái nhìn trực quan về dữ liệu. Bạn sẽ tô màu dữ liệu theo cột 'rating'. Xem các biểu đồ và rút ra thông tin bạn có thể.

# Drop rows with missing values and drop duplicate
df.dropna(inplace=True)
df.drop_duplicates(inplace=True)

# Visualize pairplot of df
sns.pairplot(df, hue='rating');

pairplot seaborn

Ví dụ, hãy xem user_rating_score theo release_year. Không có xu hướng dễ nhận thấy bằng mắt, nhưng có lẽ phân tích dữ liệu sẽ làm rõ bất kỳ xu hướng nào. Nếu bạn muốn xem một số thống kê tóm tắt của DataFrame, bạn cũng có thể dùng phương thức .describe():

# Get summary stats of df
df.describe()
 ratingdescriptionrelease_yearuser_rating_scoreuser_rating_size
count246.000000246.000000246.000000246.0
mean73.5569112010.27235881.39024480.0
std26.6161458.88721912.6778830.0
min10.0000001940.00000055.00000080.0
25%60.0000002007.00000071.00000080.0
50%80.0000002015.00000083.50000080.0
75%90.0000002016.00000092.75000080.0
max124.0000002017.00000099.00000080.0

Groupby và split-apply-combine để trả lời câu hỏi

Bước 1. Chia (Split)

Sau khi đã xem qua dữ liệu, giờ là phần thú vị. Trước hết bạn sẽ dùng phương thức groupby để chia dữ liệu thành các nhóm, mỗi nhóm là tập phim phát hành trong một năm nhất định. Đây là phần split trong split-apply-combine:

# Group by year
df_by_year = df.groupby('release_year')

Điều này tạo ra một đối tượng groupby:

# Check type of GroupBy object
type(df_by_year)
pandas.core.groupby.DataFrameGroupBy

Bước 2. Áp dụng (Apply)

Những đối tượng groupby như vậy rất hữu ích. Hãy nhớ rằng phương thức .describe() của DataFrame trả về thống kê tóm tắt cho các cột số? Vâng, phương thức .describe() cho các đối tượng DataFrameGroupBy trả về thống kê tóm tắt cho mỗi cột số, nhưng được tính riêng cho từng nhóm đã chia. Trong trường hợp của bạn, là cho mỗi release_year. Đây là ví dụ của phần apply trong split-apply-combine: bạn đang áp dụng phương thức .describe() lên mỗi nhóm trong groupby. Hãy làm và in 5 hàng đầu tiên của kết quả:

# Summary stats over years
df_by_year.describe().head()
 ratingdescriptionuser_rating_scoreuser_rating_size
 countmeanstdmin25%50%75%maxcountmean...75%maxcountmeanstdmin25%50%75%max
release_year                     
19401.035.0NaN35.035.035.035.035.01.061.0...61.061.01.080.0NaN80.080.080.080.080.0
19781.060.0NaN60.060.060.060.060.01.086.0...86.086.01.080.0NaN80.080.080.080.080.0
19821.060.0NaN60.060.060.060.060.01.068.0...68.068.01.080.0NaN80.080.080.080.080.0
19861.035.0NaN35.035.035.035.035.01.067.0...67.067.01.080.0NaN80.080.080.080.080.0
19871.060.0NaN60.060.060.060.060.01.058.0...58.058.01.080.0NaN80.080.080.080.080.0

5 hàng × 24 cột

Nếu bạn muốn xem việc nhóm trông như thế nào, bạn có thể truyền đối tượng groupby vào hàm list():

# Cast grouping as a list and check out one year
list(df_by_year)[10]
(1995,             title rating                                ratinglevel  \
 766         Balto      G  General Audiences. Suitable for all ages.   
 967  Heavyweights     PG              some rude language and pranks   

      ratingdescription  release_year  user_rating_score  user_rating_size  
 766                 35          1995               64.0                80  
 967                 60          1995               74.0                80  )

Bước 3. Kết hợp (Combine)

Giả sử bạn muốn giá trị trung bình hoặc trung vị của user_rating_score cho từng năm. Khi đó bạn có thể áp dụng lần lượt phương thức .mean() hoặc .median() lên đối tượng groupby và "kết hợp" chúng thành một DataFrame mới.

# Get median values by year and print first 5 rows
df_med_by_year = df_by_year.median()
df_med_by_year.head()
 ratingdescriptionuser_rating_scoreuser_rating_size
release_year   
194035.061.080.0
197860.086.080.0
198260.068.080.0
198635.067.080.0
198760.058.080.0

Có một điểm tinh tế quan trọng liên quan đến chỉ mục (index) của DataFrame df_med_by_year. Hãy nhớ rằng index của DataFrame gồm các nhãn hàng. Xem index của DataFrame gốc df:

# Print index of df
print(df.index)
Int64Index([  0,   2,   3,   4,   5,   6,   7,   8,   9,  10,
            ...
            908, 911, 917, 931, 962, 966, 967, 972, 973, 979],
           dtype='int64', length=246)

Index này là các số thứ tự hàng gốc, gắn nhãn bằng số nguyên. '1' bị thiếu vì bạn đã loại một số hàng bên trên. Index của df_med_by_year gồm các giá trị trong cột gốc mà bạn đã nhóm theo, tức các năm từ release_year:

# Print index
print(df_med_by_year.index)
Int64Index([1940, 1978, 1982, 1986, 1987, 1989, 1990, 1992, 1993, 1994, 1995,
            1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
            2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017],
           dtype='int64', name='release_year')

Bạn quan tâm đến cột user_rating_score, chứa trung vị đánh giá cho mỗi năm. Bạn có thể cắt riêng cột user_rating_score của df_med_by_year và vẽ nó theo năm (được cho bởi index của DataFrame df_rat_by_year):

# Slice out user rating and plot
df_rat_by_year = df_med_by_year['user_rating_score']
plt.scatter(df_rat_by_year.index, df_rat_by_year)
plt.xlabel('year of release')
plt.ylabel('median rating');

Netflix groupby split-apply-combine

Nhìn vào hình, trung vị điểm đánh giá chắc chắn tăng theo thời gian. Bạn sẽ cần vận dụng một số thống kê tinh vi hơn để thuyết phục tôi về xu hướng nói chung, nhưng đây là ví dụ cho thấy Phân tích Dữ liệu Khám phá là điểm khởi đầu tuyệt vời cho nghiên cứu tiếp theo.

Groupby và split-apply-combine trong sử dụng hằng ngày

Các đối tượng groupby không trực quan. Tuy vậy, chúng tương ứng với hành động tự nhiên là chia một bộ dữ liệu theo một trong các cột của nó (hoặc nhiều hơn một, nhưng hãy để dành cho một bài khác về nhóm theo nhiều cột và chỉ mục phân cấp).

Nguyên tắc split-apply-combine không chỉ đẹp và thực tiễn, mà còn là điều các Nhà khoa học Dữ liệu dùng hằng ngày, như trong ví dụ trên. Để thấy thêm các ứng dụng, hãy xem bài báo gốc của Hadley Wickham, The Split-Apply-Combine Strategy for Data Analysis. Nếu bạn có ý kiến, phản hồi và/hoặc suy ngẫm nào, cứ thoải mái liên hệ với tôi trên twitter: @hugobowne.

Xem thêm các hướng dẫn pandas hữu ích khác của DataCamp, bao gồm:

Chủ đề

Liên quan