06/04/2021, 14:46

Xử lý văn bản trong Pandas - Pandas

Trong bài này mình sẽ hướng dẫn cách làm việc với văn bản trong Pandas, đây là một kiểu dữ liệu rất phổ biến trong Python nói chung và Pandas nói riêng. Trong những bài trước, hầu như mình chỉ sử dụng dữ liệu số để ví dụ cho các bài học về Pandas. Tuy nhiên trong thực tế thì bên cạnh dữ liệu số ...

Trong bài này mình sẽ hướng dẫn cách làm việc với văn bản trong Pandas, đây là một kiểu dữ liệu rất phổ biến trong Python nói chung và Pandas nói riêng.

Trong những bài trước, hầu như mình chỉ sử dụng dữ liệu số để ví dụ cho các bài học về Pandas. Tuy nhiên trong thực tế thì bên cạnh dữ liệu số chắc chắn sẽ luôn có dữ liệu văn bản. Trong bài hôm nay chúng ta sẽ cùng tìm hiểu cách thao tác với dữ liệu văn bản trong Pandas nhé.

1. Kiểu dữ liệu văn bản trong Pandas

Giới thiệu về kiểu dữ liệu string

Trong Pandas, ta có thể tạo một Series từ chuỗi đơn giản như sau:

In[1]
import pandas as pd
import numpy as np

f = pd.Series(['Zaidap.com', 'Học lập trình online'])

print(f)
Out[1]
0                Zaidap.com
1    Học lập trình online
dtype: object

Ta có thể thấy dtype của Series này được Pandas chỉ định là object, đây chính là kiểu dữ liệu mặc định khi nhận một danh sách mà trong nó tồn tại giá trị không phải là số.

Trước Pandas phiên bản 1.0, khi lưu trữ văn bản trong DataFrame hay Series thì dtype duy nhất mà Pandas cung cấp đó là object. Điều này sẽ gây ra một số bất lợi sau:

  • Dễ lưu trữ lẫn lộn giữa dữ liệu văn bản và các kiểu dữ liệu khác (do object là kiểu dữ liệu chung nên bất kỳ loại dữ liệu nào cũng có thể là object).
  • Vì là kiểu dữ liệu chung chung cho nên rất khó để cụ thể hoá một số phương thức riêng cho kiểu dữ liệu văn bản. Bạn không thể thêm những phương thức liên quan đến văn bản như cắt, in hoa, in thường,... vào object được do nếu sử dụng lên dữ liệu số sẽ gây xung đột.

Từ sau phiên bản 1.0, Pandas đã bổ sung một kiểu dữ liệu dành riêng cho văn bản là StringDtype. Chúng ta có thể chỉ định cho Series trên thành kiểu này bằng 3 cách:

In[2]
print("Cách 1: 
", pd.Series(['Zaidap.com', 'Học lập trình online'], dtype=pd.StringDtype()))
print("
Cách 2: 
", pd.Series(['Zaidap.com', 'Học lập trình online'], dtype='string'))
print("
Cách 3: 
", pd.Series(['Zaidap.com', 'Học lập trình online']).astype('string'))
Out[2]
Cách 1: 
0                Zaidap.com
1    Học lập trình online
dtype: string

Cách 2: 
0                Zaidap.com
1    Học lập trình online
dtype: string

Cách 3: 
0                Zaidap.com
1    Học lập trình online
dtype: string

Nếu như bạn áp kiểu string lên danh sách trong đó có các phần tử không phải là văn bản thì chúng sẽ được ép sang chuỗi, chẳng hạn:

In[3]
f_v = pd.Series(['Zaidap.com', 99, np.nan, 'Học lập trình', 'online', True], dtype='string')

print("series: 
", f_v)

print("
Giá trị của phần tử số 1: ", f_v[1])
print("Kiểu dữ liệu của phần tử số 1: ", type(f_v[1]))
print("
Giá trị của phần tử số 5:", f_v[5])
print("Kiểu dữ liệu của phần tử số 5:", type(f_v[5]))
Out[3]
series: 
0         Zaidap.com
1               99
2             <NA>
3    Học lập trình
4           online
5             True
dtype: string

Giá trị của phần tử số 1: 99
Kiểu dữ liệu của phần tử số 1: <class 'str'>

Giá trị của phần tử số 5: True
Kiểu dữ liệu của phần tử số 5: <class 'str'>

Sự khác biệt giữa kiểu string và object

Tất nhiên với việc xuất hiện kiểu dữ liệu riêng dành cho chuỗi và văn bản, mình luôn khuyến khích các bạn sử dụng dtype này để lưu dữ liệu dạng văn bản vì nó sẽ trực quan hơn so với việc dùng object.

Có một số sự khác biệt đáng chú ý giữa kiểu object và string mà bạn cần biết, đầu tiên đó là nếu như không xuất hiện NA trong dãy (giá trị null) thì một số phương thức áp dụng cho cả 2 kiểu (sẽ nói cụ thể bên dưới) sẽ cùng trả về dtype là int64, giả sử ta sẽ đếm xem trong dãy có các phần tử nào chứa ký tự 'e' hay không:

In[4]
s = pd.Series(['f', 'r', 'e', 'e', 't', 'u', 't', 's'], dtype='string')
o = pd.Series(['f', 'r', 'e', 'e', 't', 'u', 't', 's'], dtype='object')

print("Phần tử mang giá trị = 'e' (dtype='string'): 
", s.str.count('e'))
print("
Phần tử mang giá trị = 'e' (dtype='object'): 
", o.str.count('e'))
Out[4]
Phần tử mang giá trị = 'e' (dtype='string'): 
0    0
1    0
2    1
3    1
4    0
5    0
6    0
7    0
dtype: Int64

Phần tử mang giá trị = 'e' (dtype='object'): 
0    0
1    0
2    1
3    1
4    0
5    0
6    0
7    0
dtype: int64

Sự khác biệt sẽ xuất hiện khi trong dãy có chứa NA hoặc là một giá trị khác chuỗi (số, boolean,...), khi đó dtype object sẽ trả về float64 còn string vẫn sẽ là int64:

In[5]
s = pd.Series(['f', 'r', 'e', 'e', 100, 't', 'u', 't', 's', None], dtype='string')
o = pd.Series(['f', 'r', 'e', 'e', 100, 't', 'u', 't', 's', None], dtype='object')

print("Phần tử mang giá trị = 'e' (dtype='string'): 
", s.str.count('e'))
print("
Phần tử mang giá trị = 'e' (dtype='object'): 
", o.str.count('e'))
Out[5]
Phần tử mang giá trị = 'e' (dtype='string'): 
0       0
1       0
2       1
3       1
4       0
5       0
6       0
7       0
8       0
9    <NA>
dtype: Int64

Phần tử mang giá trị = 'e' (dtype='object'): 
0    0.0
1    0.0
2    1.0
3    1.0
4    NaN
5    0.0
6    0.0
7    0.0
8    0.0
9    NaN
dtype: float64

Bên cạnh việc trả về kiểu dữ liệu khác, một điều mà ta để ý đó là phần tử số nguyên (giá trị 100) ở Series có dtype string trả về giá trị 0 (do '100' không chứa 'e'), còn với dtype là object thì sẽ trả về là NA. Lý do đơn giản là vì với dtype string thì phần tử này đã được chuyển thành chuỗi, còn phần tử kia vẫn mang giá trị là số nguyên:

In[6]
print("Phần tử: ", s[4], " có kiểu dữ liệu là: ", type(s[4]))
print("Phần tử: ", o[4], " có kiểu dữ liệu là: ", type(o[4]))
Out[6]
Phần tử:  100  có kiểu dữ liệu là:  <class 'str'>
Phần tử:  100  có kiểu dữ liệu là:  <class 'int'>

Đây là một điểm mà các bạn nên chú ý để tránh nhầm lẫn khi sử dụng dtype string so với object truyền thống.

2. Các phương thức trên kiểu dữ liệu văn bản trong Pandas

Danh sách các phương thức trong Pandas

Để truy cập các phương thức thao tác với dữ liệu văn bản trong Pandas thì ta sẽ truy cập qua thuộc tính str như sau:

In[7]
s = pd.Series(['Zaidap.com', np.nan, 'net', 'NumPy', 'Javascript', 'Học lập trình online', True, 1, 'a'], dtype='string')

print("Series: 
", s)

print("
Độ dài của các phần tử:
", s.str.len())
print("
In hoa tất cả phần tử:
", s.str.upper())
print("
In thường tất cả phần tử:
", s.str.lower())
Out[7]
Series: 
0                Zaidap.com
1                    <NA>
2                     net
3                   NumPy
4              Javascript
5    Học lập trình online
6                    True
7                       1
8                       a
dtype: string

Độ dài của các phần tử:
0       8
1    <NA>
2       3
3       5
4      10
5      20
6       4
7       1
8       1
dtype: Int64

In hoa tất cả phần tử:
0                FREETUTS
1                    <NA>
2                     NET
3                   NUMPY
4              JAVASCRIPT
5    HỌC LẬP TRÌNH ONLINE
6                    TRUE
7                       1
8                       A
dtype: string

In thường tất cả phần tử:
0                Zaidap.com
1                    <NA>
2                     net
3                   numpy
4              javascript
5    học lập trình online
6                    true
7                       1
8                       a
dtype: string

Dưới đây là bảng tất cả các phương thức trên chuỗi được hỗ trợ trong Pandas:

Các phương thức split, replace concat là những phương thức khá quan trọng và có một vài tham số riêng, do đó mình sẽ nói kỹ ở các mục dưới, còn ở đây chúng ta sẽ đến một số ví dụ với các hàm còn lại:

In[8]
s = pd.Series(['Zaidap.com', np.nan, 'net', 'lập trình', 100, 'Học lập trình online', 69, True], dtype='string')

print("Tìm từ 'lập trình' trong các phần tử: 
", s.str.find('lập trình'))
print("
Tìm ký tự 't' trong các phần tử: 
", s.str.findall('t'))
print("
Những phần tử là số: 
", s.str.isnumeric())
print("
Lặp lại phần tử 2 lần: 
", s.str.repeat(2))
print("
Hoán đổi chữ hoa / thường: 
", s.str.swapcase())
Out[8]
Tìm từ 'lập trình' trong các phần tử: 
0      -1
1    <NA>
2      -1
3       0
4      -1
5       4
6      -1
7      -1
dtype: Int64

Tìm ký tự 't' trong các phần tử: 
0    [t, t]
1      <NA>
2       [t]
3       [t]
4        []
5       [t]
6        []
7        []
dtype: object

Những phần tử là số: 
0    False
1     <NA>
2    False
3    False
4     True
5    False
6     True
7    False
dtype: boolean

Lặp lại phần tử 2 lần: 
0                            Zaidap.comZaidap.com
1                                        <NA>
2                                      netnet
3                          lập trìnhlập trình
4                                      100100
5    Học lập trình onlineHọc lập trình online
6                                        6969
7                                    TrueTrue
dtype: string

Hoán đổi chữ hoa / thường: 
0                fREETUTS
1                    <NA>
2                     NET
3               LẬP TRÌNH
4                     100
5    hỌC LẬP TRÌNH ONLINE
6                      69
7                    tRUE
dtype: string

Với phương thức get_dummies thì mục đích chính của phương thức này là trích xuất các biến giả (dummy variables) từ Series hoặc Index:

In[9]
s = pd.Series(["Zaidap.com", "Freeuts|net", np.nan, "Zaidap.com|org"], dtype="string")
print("Series: 
", s.str.get_dummies(sep='|'))

idx = pd.Index(["Zaidap.com", "Freeuts.net", np.nan, "Zaidap.com.org"], dtype="string")
print("
Index: 
", idx.str.get_dummies(sep='.'))
Out[9]
Series: 
    Zaidap.com  Freeuts  net  org
0         1        0    0    0
1         0        1    1    0
2         0        0    0    0
3         1        0    0    1

Index: 
MultiIndex([(1, 0, 0, 0),
            (0, 1, 1, 0),
            (0, 0, 0, 0),
            (1, 0, 0, 1)],
           names=['Zaidap.com', 'Freeuts', 'net', 'org'])

Ta có thể dùng phương thức này để tìm sự xuất hiện của các phần tử duy nhất ở từng vị trí trong dãy, ví dụ:

In[10]
f = pd.Series(['Zaidap.com', np.nan, 'net', 100, 'Zaidap.com', 'Học lập trình online', True, 'Zaidap.com'], dtype='string')

f.str.get_dummies()
Out[10]
   100  Zaidap.com  Học lập trình online  True  net # Tên các cột là giá trị các phần tử được sắp xếp theo thứ tự và là duy nhất
0    0         1                     0     0    0 # Phần tử 'Zaidap.com' xuất hiện tại vị trí đầu tiên
1    0         0                     0     0    0 # Không có phần tử nào tương ứng xuất hiện tại vị trí 1 do vị trí này chứa NA
2    0         0                     0     0    1 # ...
3    1         0                     0     0    0 # ...
4    0         1                     0     0    0 # Phần tử 'Zaidap.com' xuất hiện tại vị trí 4
5    0         0                     1     0    0 # ...
6    0         0                     0     1    0 # ...
7    0         1                     0     0    0 # Phần tử 'Zaidap.com' xuất hiện tại vị trí 7

Ngoài việc áp dụng với Series, ta có thể sử dụng các phương thức trên với Index trong DataFrame:

In[11]
df = pd.DataFrame({'Category': ['NumPy', 'Matplotlib', 'Pandas'],'Views': [10000, 20000, 30000]})

print("DataFrame: 
", df)

df['Category'] = df['Category'].str.upper()
df.columns = df.columns.str.lower()

print("
Chuyển cột category thành in hoa và tên các cột thành in thường: 
", df)
Out[11]
DataFrame: 
      Category  Views
0       NumPy  10000
1  Matplotlib  20000
2      Pandas  30000

Chuyển cột category thành in hoa và tên các cột thành in thường: 
      category  views
0       NUMPY  10000
1  MATPLOTLIB  20000
2      PANDAS  30000

Indexing và Extracting

Indexing

Bạn có thể dùng ký hiệu [] để index trực tiếp vào chuỗi thông qua thuộc tính str, nếu như index ra bên ngoài chuỗi thì giá trị trả về sẽ là NA:

In[12]
s = pd.Series(['Zaidap.com', 'a', 'A', 'Lập trình', None], dtype='string')

print('Index vào ký tự đầu tiên của các phần tử: 
', s.str[0])

print('
Index vào ký tự thứ 3 của các phần tử: 
', s.str[2])
Out[12]
Index vào ký tự đầu tiên của các phần tử: 
0       F
1       a
2       A
3       L
4    <NA>
dtype: string

Index vào ký tự thứ 3 của các phần tử: 
0       e
1    <NA>
2    <NA>
3       p
4    <NA>
dtype: string

Extracting

Để extracting (trích xuất) các chuỗi con trong từng phần tử thì Pandas cung cấp cho ta 2 phương thức sau:

  • extract: trích xuất giá trị khớp đầu tiên
  • extractall: trích xuất tất cả các giá trị khớp

Các phương thức trên đều chấp nhận regex làm tham số. Ta sẽ thử một ví dụ sau:

In[13]
s = pd.Series(['x1', 'z2', 'y2', 'f3'], dtype='string')

# Regex bên dưới khớp những phần tử có chứa ký tự x,y hoặc z và những số đi kèm phía sau
print(s.str.extract(r"([xyz])(d)", expand=False))
Out[13]
      0     1
0     x     1
1     z     2
2     y     2
3  <NA>  <NA>

# Phần tử cuối cùng (f3) không khớp với regex nên giá trị trả về là NA

Lưy ý rằng phương thức extract nhận tham số expand mặc định là True, với True thì kết quả trả về luôn là DataFrame, còn False thì kết quả trả về có thể là Index, Series hoặc DataFrame tuỳ thuộc vào dữ liệu và regex mà ta chỉ định:

In[14]
s = pd.Series(['x1', 'z2', 'y2', 'f3'], dtype='string')

print("Trả về DataFrame: 
", s.str.extract(r"([xyz])", expand=True))
print("
Trả về Series: 
", s.str.extract(r"([xyz])", expand=False))
Out[14]
Trả về DataFrame: 
       0
0     x
1     z
2     y
3  <NA>

Trả về Series: 
0       x
1       z
2       y
3    <NA>
dtype: string

Chúng ta còn có thể đặt tên của cột khi trích xuất như sau:

In[15]
s = pd.Series(['x1', 'z2', 'y2', 'f3'], dtype='string')

print(s.str.extract(r"(?P<chữ>[xyz])(?P<số>d)", expand=False))
Out[15]
    chữ    số
0     x     1
1     z     2
2     y     2
3  <NA>  <NA>

Khác với extract, phương thức extractall luôn luôn trả về là DataFrame và sẽ có một cột index tên là "match" chứa thứ tự xuất hiện tương ứng với regex:

In[16]
s = pd.Series(['x1x2x3', 'y1y2', 'z1', 'a1', 'c1c2'], index=['X', 'Y', 'Z', 'A', 'C'], dtype='string')
rg = "(?P<chữ>[a-z])(?P<số>[0-9])"

print("Extract: 
", s.str.extract(rg))
print("
Extract all: 
", s.str.extractall(rg))
Extract: 
   chữ số
X   x  1
Y   y  1
Z   z  1
A   a  1
C   c  1

Extract all: 
         chữ số
  match       
X 0       x  1
  1       x  2
  2       x  3
Y 0       y  1
  1       y  2
Z 0       z  1
A 0       a  1
C 0       c  1
  1       c  2

Tách (spliting)

Để tách từng phần tử trong Series/Index theo pattern nhất định ta dùng phương thức split:

In[17]
s = pd.Series(['free_tuts', 'lap_trinh', np.pi, 'pandas', None], dtype='string')

print(s.str.split('_'))
Out[17]
0           [free, tuts]
1           [lap, trinh]
2    [3.141592653589793]
3               [pandas]
4                   <NA>
dtype: object

Ta có thể lấy từng phần tử đã được tách ra bằng phương thức get hoặc index như trên:

In[18]
print(s.str.split('_').str.get(0))

print(s.str.split('_').str[0])
Out[18]
0                 free
1                  lap
2    3.141592653589793
3               pandas
4                 <NA>
dtype: object

0                 free
1                  lap
2    3.141592653589793
3               pandas
4                 <NA>
dtype: object

Ta có thể truyền tham số expand=True để trả về một DataFrame chứa từng phần tử đã được tách thay vì một mảng:

In[19]
print(s.str.split('_', expand=True))
Out[19]
                   0      1
0               free   tuts
1                lap  trinh
2  3.141592653589793   <NA>
3             pandas   <NA>
4               <NA>   <NA>

Phức tạp hơn, chúng ta có thể giới hạn số lần chia bằng tham số n:

In[20]
s = pd.Series(['free_tuts_net', 'lap_trinh_python', np.pi, 'python_pandas', None], dtype='string')

print(s.str.split('_', expand=True, n=1))
Out[20]
                   0             1
0               free      tuts_net
1                lap  trinh_python
2  3.141592653589793          <NA>
3             python        pandas
4               <NA>          <NA>

Pandas còn hỗ trợ thêm phương thức rsplit, phương thức này giống như split nhưng hoạt động ở chiều ngược lại (reverse):

In[21]
s = pd.Series(['free_tuts_net', 'lap_trinh_python', np.pi, 'python_pandas', None], dtype='string')

print(s.str.rsplit('_', expand=True, n=1))
Out[21]
                   0       1
0          free_tuts     net
1          lap_trinh  python
2  3.141592653589793    <NA>
3             python  pandas
4               <NA>    <NA>

Thay thế (replacing)

Ta sử dụng phương thức replace để thay thế tường phần tử theo tham số truyền vào, tham số có thể là chuỗi thông thường cho tác vụ đơn giản hoặc là regex cho những thứ phức tạp hơn:

In[22]
s = pd.Series(['Free_Tuts_net', 'lap_trinh_python', np.pi, 'python_pandas', None], dtype='string')

print(s.str.replace('e|^python', '.', regex=True))
Out[22]
0        Fr.._Tuts_n.t
1     lap_trinh_python
2    3.141592653589793
3             ._pandas
4                 <NA>
dtype: string

Nối (concatenate)

Nối Series thành một chuỗi

Chúng ta dùng phương thức cat để nối các phần tử trong chuỗi, chúng ta có thể truyền tham số sep để chỉ định chuỗi ngăn cách khi nối, còn không thì mặc định sẽ là chuỗi rỗng (''):

In[23]
s = pd.Series(['f','r','e','e','t','u','t','s'], dtype='string')

print("Không truyền sep: ", s.str.cat())
print("sep='|': ", s.str.cat(sep='|'))
Out[23]
Không truyền sep:  Zaidap.com
sep='|':  f|r|e|e|t|u|t|s

Nối một list với Series

Chúng ta có thể truyền vào tham số đầu tiên là một List bất kỳ (chiều dài phải tương đương Series), khi đó Series và List sẽ được ghép với nhau theo vị trí tương ứng:

In[24]
s = pd.Series(['f','r','e','e','t','u','t','s'], dtype='string')
d = ['F', 'R', 'E', 'E', 'T', 'U', 'T', 'S']

print(s.str.cat(d))
Out[24]
0    fF
1    rR
2    eE
3    eE
4    tT
5    uU
6    tT
7    sS
dtype: string

Nếu như xuất hiện NA thì ta có thể dùng tham số na_rep để điền vào các giá trị bị khuyết:

In[25]
s = pd.Series(['f','r','e','e','t','u','t','s'], dtype='string')
d = ['F', 'R', None, 'E', 'T', None, 'T', 'S']

print(s.str.cat(d, na_rep='--'))
Out[25]
0     fF
1     rR
2    e--
3     eE
4     tT
5    u--
6     tT
7     sS
dtype: string

Nối một DataFrame với Series

Ta hoàn toàn có thể nối một DataFrame với một Series tương tự như trên:

In[26]
s = pd.Series(['f','r','e','e','t','u','t','s'], dtype='string')
d = pd.Series(['F', 'R', None, 'E', 'T', None, 'T', 'S'])
f = pd.Series(np.arange(8), dtype='string')

s.str.cat(pd.concat([d, f], axis=1), na_rep='-')
Out[26]
0    fF0
1    rR1
2    e-2
3    eE3
4    tT4
5    u-5
6    tT6
7    sS7
dtype: string

Nối các object với Index có trật tự

Chúng ta có thể nối các object (DataFrame, Series) với nhau với trật tự của Index khác nhau. Khi đó tuỳ thuộc vào tham số join (mặc định là 'left'): 'left', 'outer', 'inner' hoặc 'right' mà kết quả sẽ khác nhau:

In[27]
s = pd.Series(['f','r','e','e'], dtype='string')
f = pd.Series([1, 2, 3, 4], index=[1, 3, 2, 0], dtype='string')

print("s Series: 
", s)
print("f Series: 
", f)

print("join = left: 
", s.str.cat(f))
print("
join = right: 
", s.str.cat(f, join='right'))
print("
join = inner: 
", s.str.cat(f, join='inner'))
print("
join = outer: 
", s.str.cat(f, join='outer'))
Out[27]
s Series: 
 0    f
1    r
2    e
3    e
dtype: string

f Series: 
 1    1
3    2
2    3
0    4
dtype: string


join = left: 
 0    f4
1    r1
2    e3
3    e2
dtype: string

join = right: 
 1    r1
3    e2
2    e3
0    f4
dtype: string

join = inner: 
 0    f4
1    r1
2    e3
3    e2
dtype: string

join = outer: 
 0    f4
1    r1
2    e3
3    e2
dtype: string

3. Ví dụ thực tế

Chúng ta sẽ đi đến một ví dụ thực tế, bộ dữ liệu ta sử dụng sẽ là dữ liệu về tên và một số thông tin liên quan của các hành khách trên tàu Titanic, các bạn có thể tải về ở đây.

Đầu tiên ta sẽ import dữ liệu và xem qua một số cột của tập dữ liệu này:

In[28]
titanic = pd.read_excel('./titanic_name.xlsx')

print(titanic.head())
Out[28]
   Sống sót  Khoang                                                Tên  
0         0       3                            Braund, Mr. Owen Harris   
1         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...   
2         1       3                             Heikkinen, Miss. Laina   
3         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)   
4         0       3                           Allen, Mr. William Henry   

  Giới tính  Tuổi                Vé   Giá vé  
0      male  22.0         A/5 21171   7.2500  
1    female  38.0          PC 17599  71.2833  
2    female  26.0  STON/O2. 3101282   7.9250  
3    female  35.0            113803  53.1000  
4      male  35.0            373450   8.0500  

Ta có thể thấy họ và tên của những người trên tàu được phân cách bởi dấu phẩy, vậy ta có thể lấy được họ bằng cách:

In[29]
surname = titanic['Tên'].str.split(',')

print(surname)
Out[29]
0                             [Braund,  Mr. Owen Harris]
1      [Cumings,  Mrs. John Bradley (Florence Briggs ...
2                              [Heikkinen,  Miss. Laina]
3        [Futrelle,  Mrs. Jacques Heath (Lily May Peel)]
4                            [Allen,  Mr. William Henry]
                             ...                        
886                             [Montvila,  Rev. Juozas]
887                      [Graham,  Miss. Margaret Edith]
888          [Johnston,  Miss. Catherine Helen "Carrie"]
889                             [Behr,  Mr. Karl Howell]
890                               [Dooley,  Mr. Patrick]
Name: Tên, Length: 891, dtype: object

Và ta dễ dàng thêm một cột mới chứa họ của hành khách như sau:

In[30]
titanic['Họ'] = surname.str.get(0)
print(titanic['Họ'])
Out[30]
0         Braund
1        Cumings
2      Heikkinen
3       Futrelle
4          Allen
         ...    
886     Montvila
887       Graham
888     Johnston
889         Behr
890       Dooley
Name: Họ, Length: 891, dtype: object

Ở trong cột "Tên", ta có thể thấy trước tên sẽ là danh xưng tương ứng, chẳng hạn như Mr., Mrs., Dr.,... Ta có thể sử dụng phương thức contains để tìm ra những hành khách là Tiến sĩ trên tàu Titanic:

In[31]
dr_list = titanic['Tên'].str.contains('Dr.')

print("Dr. List: 
", dr_list)
print("
Tổng số tiến sĩ trên tàu: ", dr_list.sum())
print("
Danh sách những hành khách là tiến sĩ: ", titanic[dr_list])
Out[31]
Dr. List: 
0      False
1      False
2      False
3      False
4      False
       ...  
886    False
887    False
888    False
889    False
890    False
Name: Tên, Length: 891, dtype: bool

Tổng số tiến sĩ trên tàu:  11

Danh sách những hành khách là tiến sĩ:       Sống sót  Khoang                                              Tên  
47          1       3                        O'Driscoll, Miss. Bridget   
130         0       3                             Drazenoic, Mr. Jozef   
245         0       1                      Minahan, Dr. William Edward   
317         0       2                             Moraweck, Dr. Ernest   
398         0       2                                 Pain, Dr. Alfred   
416         1       2  Drew, Mrs. James Vivian (Lulu Thorne Christian)   
632         1       1                        Stahelin-Maeglin, Dr. Max   
660         1       1                    Frauenthal, Dr. Henry William   
679         1       1               Cardeza, Mr. Thomas Drake Martinez   
766         0       1                        Brewe, Dr. Arthur Jackson   
796         1       1                      Leader, Dr. Alice (Farnham)   

    Giới tính  Tuổi        Vé    Giá vé                Họ  
47     female   NaN     14311    7.7500        O'Driscoll  
130      male  33.0    349241    7.8958         Drazenoic  
245      male  44.0     19928   90.0000           Minahan  
317      male  54.0     29011   14.0000          Moraweck  
398      male  23.0    244278   10.5000              Pain  
416    female  34.0     28220   32.5000              Drew  
632      male  32.0     13214   30.5000  Stahelin-Maeglin  
660      male  50.0  PC 17611  133.6500        Frauenthal  
679      male  36.0  PC 17755  512.3292           Cardeza  
766      male   NaN    112379   39.6000             Brewe  
796    female  49.0     17465   25.9292            Leader  

Ta thấy cột giới tính có 2 giá trị là male female là tiếng Anh trong khi tên cột lại là tiếng Việt. Chúng ta có thể dùng phương thức replace để sửa lại:

In[32]
titanic['Giới tính'] = titanic['Giới tính'].replace({'male': 'Đàn ông', 'female': 'Phụ nữ'})

print(titanic.head())
Out[32]
   Sống sót  Khoang                                                Tên  
0         0       3                            Braund, Mr. Owen Harris   
1         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...   
2         1       3                             Heikkinen, Miss. Laina   
3         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)   
4         0       3                           Allen, Mr. William Henry   

  Giới tính  Tuổi                Vé   Giá vé         Họ  
0   Đàn ông  22.0         A/5 21171   7.2500     Braund  
1    Phụ nữ  38.0          PC 17599  71.2833    Cumings  
2    Phụ nữ  26.0  STON/O2. 3101282   7.9250  Heikkinen  
3    Phụ nữ  35.0            113803  53.1000   Futrelle  
4   Đàn ông  35.0            373450   8.0500      Allen  

Sử dụng phương thức len ta có thể tìm được hành khách có tên dài nhất và ngắn nhất trên tàu Titanic:

In[33]
name_len = titanic['Tên'].str.len()

idx_max = name_len.idxmax()
print("Người có tên dài nhất:", titanic['Tên'][idx_max])
idx_min = name_len.idxmin()
print("Người có tên ngắn nhất:", titanic['Tên'][idx_min])
Out[33]
Người có tên dài nhất: Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y Vallejo)
Người có tên ngắn nhất: Lam, Mr. Ali

Hành khách trên tàu có tên dài nhất là Mrs. Maria Josefa Perezde Soto y Vallejo Peñasco y Castellana đến từ Tây Ban Nha, còn người có tên ngắn nhất là Mr Ali Lam, một du khách tới từ Hong Kong, cả 2 đều được cứu sống sau vụ tai nạn này.

4. Tổng kết

Vậy là ta đã hoàn thành bài học về dữ liệu kiểu văn bản trong Pandas, đây là một kiểu dữ liệu rất phổ biến bên cạnh dữ liệu số thường thấy. Với bộ dữ liệu trên các bạn hãy thử thêm nhiều thao tác hơn với những phương thức mình đã giới thiệu ở trên để hiểu sâu hơn nhé. Hẹn gặp các bạn ở bài tiếp theo.

Bài tiếp
0