pandasの要素としてリストを格納し処理
pandas.DataFrame
, Series
の要素としてPythonの組み込み型であるリストlist
を格納できる。
例えば区切り文字(デリミタ)で区切られた文字列などは、文字列メソッドで処理するよりもリスト化したほうが便利な場合がある。
ここでは、以下の内容について説明する。
pandas.DataFrame
の文字列の列からリストの列を生成- リストの列に関数・メソッドを適用
- リストの要素数を取得
- リストをソート
- リストを文字列に連結
- リストから要素を追加・削除
- リストに対する条件で行を抽出
リストとpandas.DataFrame
, Series
を変換する方法については以下の記事を参照。
以下のpandas.DataFrame
を例とする。
import pandas as pd
df = pd.DataFrame({'s': ['X,Y,Z', 'X', 'XY,Y', 'Y,Z,XY']},
index=['a', 'b', 'c', 'd'])
print(df)
# s
# a X,Y,Z
# b X
# c XY,Y
# d Y,Z,XY
pandas.DataFrameの文字列の列からリストの列を生成
文字列メソッドstr.split()
を使う。引数expand=True
とすると複数の列に分割されるが、デフォルト(expand=False
)の場合はリストとなる。
df['l'] = df['s'].str.split(',')
print(df)
# s l
# a X,Y,Z [X, Y, Z]
# b X [X]
# c XY,Y [XY, Y]
# d Y,Z,XY [Y, Z, XY]
列のデータ型dtype
は文字列の列もリストの列もobject
だが、各要素はそれぞれの型type
になっている。
print(df.dtypes)
# s object
# l object
# dtype: object
print(type(df.at['a', 's']))
# <class 'str'>
print(type(df.at['a', 'l']))
# <class 'list'>
なお、X,Y,Z
ではなくX, Y, Z
のように区切り文字のあとにスペースがある場合はstrip()
で除外する。この例の場合は変化なし。
無名関数で処理を定義して、apply()
で各要素に適用している。map()
でもOK。
- 関連記事: Python, splitでカンマ区切り文字列を分割、空白を削除しリスト化
- 関連記事: Pythonのlambda(ラムダ式、無名関数)の使い方
- 関連記事: pandasで要素、行、列に関数を適用するmap, applymap, apply
print(df['s'].apply(lambda x: [s.strip() for s in x.split(',')]))
# a [X, Y, Z]
# b [X]
# c [XY, Y]
# d [Y, Z, XY]
# Name: s, dtype: object
スペースの数が同じ場合はstr.split(', ')
のように区切り文字を指定しても問題ない。
リストの列に関数・メソッドを適用
Pythonのリストlist
型に対する関数およびメソッドをpandas.DataFrame
の列(= pandas.Series
)のapply()
またはmap()
で適用する。
要素を引数としないメソッド(リスト自体のメソッドなど)は無名関数(ラムダ式)を使う。
リストの要素数を取得
組み込み関数len()
を適用。
print(df['l'].apply(len))
# a 3
# b 1
# c 2
# d 3
# Name: l, dtype: int64
リストをソート
組み込み関数sorted()
を適用。
print(df['l'].apply(sorted))
# a [X, Y, Z]
# b [X]
# c [XY, Y]
# d [XY, Y, Z]
# Name: l, dtype: object
リストを文字列に連結
文字列メソッドjoin()
を適用。
print(df['l'].apply(','.join))
# a X,Y,Z
# b X
# c XY,Y
# d Y,Z,XY
# Name: l, dtype: object
ソートしてから文字列に連結。
print(df['l'].apply(lambda x: ','.join(sorted(x))))
# a X,Y,Z
# b X
# c XY,Y
# d XY,Y,Z
# Name: l, dtype: object
リストから要素を追加・削除
リストのメソッドappend()
で要素を追加。もとのリストに新たな要素が追加される。
df['l'].apply(lambda x: x.append('A'))
print(df)
# s l
# a X,Y,Z [X, Y, Z, A]
# b X [X, A]
# c XY,Y [XY, Y, A]
# d Y,Z,XY [Y, Z, XY, A]
リストのメソッドremove()
で要素を削除。remove()
は存在しない要素を指定するとエラーになるので、三項演算子を使って対象の要素が存在する場合のみremove()
を適用している。
df['l'].apply(lambda x: x.remove('Z') if 'Z' in x else x)
print(df)
# s l
# a X,Y,Z [X, Y, A]
# b X [X, A]
# c XY,Y [XY, Y, A]
# d Y,Z,XY [Y, XY, A]
リストに対する条件で行を抽出
リストに対してbool
値(True
, False
)を返す関数などを適用し取得したbool
型のpandas.Series
をもとのpandas.DataFrame
に渡す。
特定の要素が存在する場合に抽出。in
を使う。
print(df['l'].apply(lambda x: 'X' in x))
# a True
# b True
# c False
# d False
# Name: l, dtype: bool
print(df[df['l'].apply(lambda x: 'X' in x)])
# s l
# a X,Y,Z [X, Y, A]
# b X [X, A]
常にリスト化したほうが良いわけではなく、条件によってはもとの文字列に対して文字列メソッドstr.contains()
を使うだけでOKな場合もある。
print(df['s'].str.contains('Z'))
# a True
# b False
# c False
# d True
# Name: s, dtype: bool
print(df[df['s'].str.contains('Z')])
# s l
# a X,Y,Z [X, Y, A]
# d Y,Z,XY [Y, XY, A]
X
とXZ
のように区切り文字で区切られた要素の一部分が重複していたりするとstr.contains()
だと面倒。