【Python】文字列・検索・大小区分・部分一致

## この記事でわかること
Python で文字列や文字列のリストを検索する時に，
- 大小を区別して検索するのか
- 部分一致なのか完全一致なのか

以上のことがわかるようになります．
また，忘れたときにこの記事を見てすぐ確認できます．

## 今回検索に使用するメソッド・演算子
|メソッド・演算子|説明|
|--|--|
|find|文字列の検索に使用|
|in|文字列・リストの検索に使用|
|contains|DataFrame の検索に使用|
|isin|DataFrame の検索に使用|

## 早めの結論
### 大小区別
- 基本的に大小を区別して検索する
- 区別したくない場合は lower 関数を使用するなど工夫する

### 部分一致
- 文字列は部分一致する
- リストは部分一致しない（完全一致）
- 正規表現を使用すればリストでも部分一致する
- Dataframe は contains メソッドで部分一致する
- Dataframe は isin メソッドで完全一致する

## 文字列の検索
### 文字列の大小区別
```Python
# 検索対象 ("applebananalemon")
target_str = "apple" "banana" "lemon"

# find メソッド
print(target_str.find("apple")) # 検索される
print(target_str.find("APPLE")) # 検索されない
print(target_str.lower().find("APPLE".lower())) # 検索される

# in 演算子
print("ap" in target_str) # 検索される
print("AP" in target_str) # 検索されない
```

出力結果
```Python
0
-1
True
False
```
このように，find メソッドや in 演算子で文字列を検索すると，
大小区別して検索されます．
大小区別したくない場合は，3 つめの例のように，
lower メソッドを使って大小を変換してから検索すると良いです．

### 文字列の部分一致
```Python
# 検索対象 ("applebananalemon")
target_str = "apple" "banana" "lemon"

# find メソッド
print(target_str.find("ap")) # 検索される

# in 演算子
print("ap" in target_str) # 検索される
```

出力結果
```Python
0
True
```

このように，一部が一致している場合でも検索されます．

## リスト
### 文字列リストの大小区別
```Python
# 検索対象
target_list = ["apple", "banana", "lemon"]

# in 演算子
print("apple" in target_list) # 検索される
print("APPLE" in target_list) # 検索されない
```

出力結果
```Python
True
False
```
このように，文字列リストの検索でも大小が区別されて検索されます．

### 文字列リストの部分一致
```Python
import re

# 検索対象
target_list = ["apple", "banana", "lemon"]

# in 演算子
print("ap" in target_list) # 検索されない
print([re.search(".*ap.*", t) for t in target_list]) # 検索される（正規表現）
print(["ap" in t for t in target_list]) # 検索される（for-in 文）
```

出力結果
```Python
False
[<re.Match object; span=(0, 5), match='apple'>, None, None]
[True, False, False]
```
このように，文字列リストの検索はそのままやると部分一致しません．
部分一致で検索したい場合は, for-in 文を使ったり正規表現を使用すると良さそうです．

## DataFrame
### DataFrame の大小区別
```Python
import pandas as pd

# 検索対象
target_series = pd.Series(["apple", "banana", "lemon"])

# contains メソッド
print(target_series.str.contains("apple")) # 検索される
print(target_series.str.contains("APPLE")) # 検索されない

# isin メソッド
print(target_series.isin(["apple"])) # 検索される
print(target_series.isin(["APPLE"])) # 検索されない
```

出力結果
```Python
0     True
1    False
2    False
dtype: bool
0    False
1    False
2    False
dtype: bool
0     True
1    False
2    False
dtype: bool
0    False
1    False
2    False
dtype: bool
```

このように，contains でも isin でも大小区別されて検索されます．

### 部分一致
```Python
import pandas as pd

# 検索対象
target_series = pd.Series(["apple", "banana", "lemon"])

# contains メソッド
print(target_series.str.contains("ap")) 検索される

# isin メソッド
print(target_series.isin(["ap"])) # 検索されない
```

出力結果
```Python
0     True
1    False
2    False
dtype: bool
0    False
1    False
2    False
dtype: bool
```
このように，contins は部分一致で検索でき，isin は部分一致で検索されません．

## まとめ
表にまとめると以下です．
|対象|メソッド・演算子|大小区別|部分一致|
|--|--|--|--|
|文字列|find・in|する|する|
|文字列リスト|in|する|しない|
|DataFrame|contains|する|する|
|DataFrame|isin|する|しない|

また大小区別したい場合は lower 関数を使うなど工夫が必要です．

## おまけ
SQL の LIKE みたいに検索する時 Python だとどうすればいいのかな気になったのが動機でこの記事を書きました．
Python の場合，検索の方法が SQL よりも多いので頭がこんがらがっていた今日この頃です．
この記事にまとめたので，頭がスッキリしました．
```SQL
検索対象 LIKE '%検索する文字%' -- 大小区別したあいまい検索
検索対象 ILIKE '%検索する文字%' -- 大小区別しないあいまい検索
```