【pandas】列名に特定の文字列を含まない列を抽出する【Python】
列名に特定の文字列を含む列を抽出するのは簡単だが、特定の文字列を含まない文字列を抽出する場合、単純にはいかない。
列名に特定の文字列を含まない列を抽出する方法はfilterと正規表現の否定先読み、否定後読みを使うことでできる。
データ作成
以下のように列名とインデックスを持つDataFrameを作成する。
1import pandas as pd
2
3data = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [8, 9, 10, 11, 12]]
4columns = ["colA", "colB", "col_C", "列D", "列_E"]
5index = ["indexA", "indexB", "index_C", "index_d"]
6df = pd.DataFrame(data=data, columns=columns, index=index)
7print(df)
8# colA colB col_C 列D 列_E
9# indexA 1 2 3 4 5
10# indexB 2 3 4 5 6
11# index_C 3 4 5 6 7
12# index_d 8 9 10 11 12
列名に特定の文字列を含まない列を抽出したい場合、特定の文字列が「先頭」「文中」「末尾」のどこに含まれるかによって指定方法が異なる。
正規表現の基本
本記事で使用する正規表現は以下の通り。
特殊文字 | 意味 |
---|---|
^ | 先頭 |
$ | 末尾 |
.(ドット) | 任意の1文字 |
* | 直前の正規表現をできるだけ多く繰り返したものにマッチ |
\w | 任意の1文字と_(アンダースコア)にマッチ |
1# 先頭が「col」で始まる文字列にマッチするパターン
2"^col.*"
3
4# 末尾が「_1文字」で終わる文字列にマッチするパターン
5".*_\w$"
列名が特定の文字列で始まらない列を抽出
否定先読みは(?!マッチさせたくない文字列)
で作成する。
マッチさせたくない文字列が先頭からの場合、"^(?!マッチさせたくない文字列).*"
で抽出可能。
1# 列名がcolで始まらない列を抽出
2df_filter = df.filter(regex="^(?!col).*")
3print(df_filter)
4# 列D 列_E
5# indexA 4 5
6# indexB 5 6
7# index_C 6 7
8# index_d 11 12
filterのregexに"^(?!col).*"
と指定することで、「col」から始まる列以外を抽出できる。
列名が特定の文字列で終わらない列を抽出
特定の文字列で終わらない列名を抽出したい場合は否定後読みという指定方法を使う。
(?<!マッチさせたくない文字列)
が否定後読みとなる。
具体的な例は以下の通り。
1# 列名が_1文字で終わらない列を抽出
2df_filter = df.filter(regex="^.*(?<!_\w)$")
3print(df_filter)
4# colA colB 列D
5# indexA 1 2 4
6# indexB 2 3 5
7# index_C 3 4 6
8# index_d 8 9 11
(?<!_\w)$
は末尾が「_1文字」で終わらないことを意味する。
^.*
は先頭からの任意の文字列を意味する。
列名が特定の文字列を含まない列を抽出
列名の真ん中に特定の文字列が含まれない列を抽出したい場合は、否定先読みを使う。
「.*
」は任意の文字列を表すが、否定先読みの中に「.*
」を入れることで、文中の特定の文字列を表せる。
1# 列名に「ol」が含まれない列を抽出
2df_filter = df.filter(regex="^(?!.*ol).*$")
3print(df_filter)
4# 列D 列_E
5# indexA 4 5
6# indexB 5 6
7# index_C 6 7
8# index_d 11 12
列名に特定の文字列を含む列を抽出
列名に特定の文字列を含む列を抽出する場合、likeという引数も使えるため、likeで抽出できるものはlikeで抽出すると楽。
filterのlikeを使う方法
指定した文字列が含まれる列をlikeで抽出
1# likeを指定することで、指定した文字列を含む列を抽出できる
2df_filter = df.filter(like="ol")
3print(df_filter)
4# colA colB col_C
5# indexA 1 2 3
6# indexB 2 3 4
7# index_C 3 4 5
8# index_d 8 9 10
対象の列がない場合はエラーではなくemptyが返ってくる
1# 指定した文字が含まれる列がない場合、indexのみのDFが返ってくる
2df_filter = df.filter(like="a")
3print(df_filter)
4# Empty DataFrame
5# Columns: []
6# Index: [indexA, indexB, index_C, index_d]
抽出対象となる列がなかった場合、エラーではなくindexだけのDataFrameが返ってくる。
indexに対しても抽出可能
1# 明示的にaxisを指定することでindexに対してもlikeでの抽出を行える
2df_filter = df.filter(like="index_", axis=0)
3print(df_filter)
4# colA colB col_C 列D 列_E
5# index_C 3 4 5 6 7
6# index_d 8 9 10 11 12
引数のaxisに0を指定することでindexに対しての抽出も行える。
filterのregexを使う方法
否定先読み、否定後読みを使わずに、正規表現にマッチする列を抽出する例。
1# 列名が2文字か3文字の列を抽出
2df_filter = df.filter(regex="^\w{2,3}$")
3print(df_filter)
4# 列D 列_E
5# indexA 4 5
6# indexB 5 6
7# index_C 6 7
8# index_d 11 12
まとめ
否定先読み、否定後読みを使うことで、「特定の文字列にマッチしない」を表現できる。
1# 列名がcolで始まらない列を抽出
2df_filter = df.filter(regex="^(?!col).*")
3
4# 列名が_1文字で終わらない列を抽出
5df_filter = df.filter(regex="^.*(?<!_\w)$")
6
7# 列名に「ol」が含まれない列を抽出
8df_filter = df.filter(regex="^(?!.*ol).*$")