医療職からデータサイエンティストへ

統計学、機械学習に関する記事をまとめています。

Pythonで前処理〜Pandasによる文字列処理まとめ〜

文字列処理

Pandasで前処理を行う際に、文字列の処理に戸惑うことがよくあります。今回は、よく使いそうな文字列処理の方法をまとめていきます。ちなみに前処理全般については過去にまとめましたので、よろしければご覧ください。

www.medi-08-data-06.work

また、Rユーザーの方はこちらを

www.medi-08-data-06.work

データの作成

まずは今回文字列処理を行うためのデータセットを作成します。よくありがちな、一つのセルに複数の単語が入っていたり、大文字小文字なども汚いデータを想定します。

import pandas as pd
import numpy as np

register = ['name  TANAKA\nnumber 1', 'name  TANAKA\nnumber 1','name  SATO',
            'name  YAMADA\nnumber 3','name  KATO\nnumber 4']
fruits = ['apple 5', 'orange 4', 'Grape 3', 'Orange 10', np.nan]

df = pd.DataFrame({'register':register, 'fruits':fruits})
df.head()
    register    fruits
0  name TANAKA\nnumber 1  apple 5
1  name TANAKA\nnumber 1  orange 4
2  name SATO   Grape 3
3  name YAMADA\nnumber 3  Orange 10
4  name KATO\nnumber 4    NaN

文字数カウント、大文字小文字変換

Pnadasで文字列処理を行うためには、strアクセサを使います。使い方はSeries.str.hogehogeです。基本的にはPandasのSeries形に対して処理を行います。とりあえず、文字列の長さ計算や、特定文字の個数カウント、大文字小文字変換など基本的な操作をやってみましょう。

  • str.len() : 文字数の数を数える。
  • str.count() : 指定の文字数をカウント
  • str.lower():小文字に変換
  • str.upper():大文字に変換
  • str.title():1文字目を大文字に変換
  • str.swapcase():大文字小文字の入れ替え
#文字数カウント
print(df['register'].str.len())
>0    21
1    21
2    10
3    21
4    19
Name: register, dtype: int64

#特定の文字をカウント
print(df['register'].str.count('T'))
>0    1
1    1
2    1
3    0
4    1
Name: register, dtype: int64

#小文字
print(df['fruits'].str.lower())
>0     apple 5
1    orange 4
2     grape 3
3    orange 8
4         NaN
Name: fruits, dtype: object

#大文字
print(df['fruits'].str.upper())
>0     APPLE 5
1    ORANGE 4
2     GRAPE 3
3    ORANGE 8
4         NaN
Name: fruits, dtype: object

#最初の文字を大文字
print(df['fruits'].str.title())
>0     Apple 5
1    Orange 4
2     Grape 3
3    Orange 8
4         NaN
Name: fruits, dtype: object

#大文字小文字入れ替え
print(df['fruits'].str.swapcase())

>0     APPLE 5
1    ORANGE 4
2     gRAPE 3
3    oRANGE 8
4         NaN
Name: fruits, dtype: object

文字列の結合・分解

次に指定の文字列を結合・分解して新たな列として追加してみます。この辺りはデータの前処理ではよく使います。文字列の分解では、expandを指定すると、結果をSeries型とDataFrame型で指定できます。

  • str.cat() : 文字列を結合
  • str.split() : 文字列を分解
#register列とfruits列を結合
#sepは結合させる文字
#na_repを指定すると欠損値も扱える。
df['register'].str.cat(df['fruits'],sep=' is ',na_rep='欠損です。')

>0      name  TANAKA\nnumber 1 is apple 5
1     name  TANAKA\nnumber 1 is orange 4
2                  name  SATO is Grape 3
3    name  YAMADA\nnumber 3 is Orange 10
4          name  KATO\nnumber 4 is 欠損です。
Name: register, dtype: object

#register列を改行\nで分解
#expand=Trueは結果をデータフレーム型で返す。
tmp = df['register'].str.split('\n',expand=True)

#さらに空白で分解してデータフレームとして結合
pd.concat([tmp[0].str.split(expand=True),tmp[1].str.split(expand=True)])
0  1
0  name    TANAKA
1  name    TANAKA
2  name    SATO
3  name    YAMADA
4  name    KATO
0  number  1
1  number  1
2  None None
3  number  3
4  number  4

文字列の抽出

次に指定の文字列を含むかどうかの判断させたり、抽出したりしてみます。str.containsstr.extractでは正規表現が使えます。

www.medi-08-data-06.work

  • str.contains(), str.startstiwh(), str.endsstiwh() : 指定の文字列を含むかどうか
  • str.extract() : 指定の文字列を抽出
#TAもしくはYAを含む
print(df["register"].str.contains(r'(TA|YA)', regex=True))
>0     True
1     True
2    False
3     True
4    False

#naで始まる
print(df["register"].str.startswith('na'))
>Name: register, dtype: bool
0    True
1    True
2    True
3    True
4    True
Name: register, dtype: bool

#3でおわる
print(df["register"].str.endswith('3'))
>0    False
1    False
2    False
3     True
4    False
Name: register, dtype: bool

#抜き出し系
#数字の抜き出し
df['fruits'].str.extract(r'(\d+)',expand=True)
    0
0  5
1  4
2  3
3  10
4  NaN

#文字列と数字を別々に抜き出し
df['fruits'].str.extract(r'(\D+)(\d+)',expand=True)
0  1
0  apple   5
1  orange  4
2  Grape   3
3  Orange  10
4  NaN NaN

#列名を指定して抜き出し
#(?P<列名>hogehoge)とする。

df['fruits'].str.extract(r'(?P<fruits>\D+)(?P<number>\d+)',expand=True)
    fruits  num
0  apple   5
1  orange  4
2  Grape   3
3  Orange  10
4  NaN NaN

文字の加工

最後に文字を置換したり、長さをそろえたりしてみます。

  • str.pad():文字の長さを揃える。
  • str.replace:文字の置換
>数字を抜き出し、3文字になるように左側を0で埋める。
df['fruits'].str.extract(r'(\d+)',expand=False).str.pad(width=3, side='left', fillchar='0')
>0    005
1    004
2    003
3    010
4    NaN
Name: fruits, dtype: object

#orangeをBananaに置換
#orangeは正規表現で

df['fruits'].str.replace(r'(.range)', 'Banana')
>0     apple 5
1    Banana 4
2     Grape 3
3    Banana 8
4         NaN
Name: fruits, dtype: object

まとめ

Pythonではstrアクセサを使うと簡単に文字列処理を行うことができます。特に、自然言語処理などでは役立ちそうですね。

※本記事は筆者が個人的に学んだこと感じたことをまとめた記事になります。所属する組織の意見・見解とは無関係です。

参考

Series — pandas 1.2.4 documentation