【Pandas】単位を削除して数値に変換する【Python】
データクレンジングをしていると、「10分」「100円」「$10.5」などの単位がついているデータに出会うことが割とよくある。
単位は非常に重要な情報だが、数値計算したい時には完全に不要になる。
整数や小数ぐらいなら、割と簡単だが、負数やマイナス記号と数字の間にスペースなどが入ってきたりすると、少しずつ面倒になってくる。
本記事ではextractで正規表現を使った抽出をメインに使う。
単位を削除して数値(整数)に変換する
1import pandas as pd
2
3df = pd.DataFrame({
4 "time": ["1分", "10分", "30分", "60分"]
5})
6print(df)
7# time
8# 0 1分
9# 1 10分
10# 2 30分
11# 3 60分
12
13# \d+で数字が1回以上出現するパターンを抽出する
14df["minutes"] = df["time"].str.extract('(\d+)').astype(int)
15print(df)
16# time minutes
17# 0 1分 1
18# 1 10分 10
19# 2 30分 30
20# 3 60分 60
こういったデータであれば、割と簡単だが、int型が簡単に通用するのは欠損値がない場合のみ。
単位を削除して数値(小数)に変換する
1import pandas as pd
2
3df = pd.DataFrame({
4 "price": ["$ 1.1", "$ 10.5", "$ 10"]
5})
6print(df)
7# price
8# 0 $ 1.1
9# 1 $ 10.5
10# 2 $ 10
11
12# \d+は整数部分の数字。\.?は小数点がある場合に抽出。\d*は小数部分がある場合に抽出
13df["price_float"] = df["price"].str.extract('(\d+\.?\d*)').astype(float)
14print(df)
15# price price_float
16# 0 $ 1.1 1.1
17# 1 $ 10.5 10.5
18# 2 $ 10 10.0
正規表現に小数点を取得する処理(\.?)と小数点以下の数字を取得する処理(\d*)を追加している。
astypeでfloatに変換しているので、この場合は欠損値があってもOK。
データに欠損値が含まれる場合の処理
int型に変換する場合
int型に変換したい場合、欠損値があるとValueErrorとなるため、欠損値補間をしてからintに変換することになる。
欠損値はNaNとなるが、NaNはfloat型なのでintにはできない。
1import pandas as pd
2
3df = pd.DataFrame({
4 "time": ["1分", "10分", "", "60分"]
5})
6print(df)
7# time
8# 0 1分
9# 1 10分
10# 2
11# 3 60分
12
13# 欠損値がある場合NaNになるが、これはfloat型のため、intにしようとするとエラーとなる
14df["minutes"] = df["time"].str.extract('(\d+)').astype(int)
15# ValueError: cannot convert float NaN to integer
欠損値をfillna()などで適当な整数で埋めてからintに変換する。
1df["minutes"] = df["time"].str.extract('(\d+)').fillna("0").astype(int)
2print(df)
3# time minutes
4# 0 1分 1
5# 1 10分 10
6# 2 0
7# 3 60分 60
上記の例では0で補完しているが、対象データ内で欠損値として適切な値で整数であれば何でもいい。
float型に変換する場合
float型に変換する場合、欠損値があってもNaNとして扱えるので、特に何か対応する必要はない。
1import pandas as pd
2
3df = pd.DataFrame({
4 "price": ["$ 1.1", "$ 10.5", ""]
5})
6print(df)
7# price
8# 0 $ 1.1
9# 1 $ 10.5
10# 2
11
12# floatにする場合、欠損値はNaNになる
13df["price_float"] = df["price"].str.extract('(\d+\.?\d*)').astype(float)
14print(df)
15# price price_float
16# 0 $ 1.1 1.1
17# 1 $ 10.5 10.5
18# 2 NaN
負数が含まれるデータの単位削除
基本的には正規表現でマイナス記号も抽出するように修正する。
数字の直前にマイナス記号がある場合
正規表現に-?を追加するだけで抽出可能。
1import pandas as pd
2
3df = pd.DataFrame({
4 "price": ["1.1円", "10.5円", "10円", "-10.0円", ""]
5})
6print(df)
7# price
8# 0 1.1円
9# 1 10.5円
10# 2 10円
11# 3 -10.0円
12# 4
13
14# -?でマイナスが0回か1回含まれるパターンを抽出
15df["price_float"] = df["price"].str.extract('(-?\d+\.?\d*)').astype(float)
16print(df)
17# price price_float
18# 0 1.1円 1.1
19# 1 10.5円 10.5
20# 2 10円 10.0
21# 3 -10.0円 -10.0
22# 4 NaN
この例ではマイナス記号のすぐ後に数字がある場合パターンのため、抽出したパターンはそのまま数値に変換できる。
しかし、抽出したパターンをそのまま数値に変換できない場合、extractで抽出してからreplaceなどで数字に変換できない部分を置換するなどが必要になる。
数字とマイナス記号の間に空白がある場合
スペースが含まれていると、そのままではfloatに変換できないので、スペースを削除する必要がある。
1import pandas as pd
2
3df = pd.DataFrame({
4 "price": ["1.1円", "10.5円", "10円", "-10.0円", "- 10円"]
5})
6print(df)
7# price
8# 0 1.1円
9# 1 10.5円
10# 2 10円
11# 3 -10.0円
12# 4 - 10 円
13
14# \s*でスペースが0回以上ある場合に対応。
15# extractはDataFrameを返すため、列0に対してreplaceでスペースを削除する
16# replaceでスペースを削除せずにfloatにしようとするとエラーとなる
17df["price_float"] = df["price"].str.extract('(-?\s*\d+\.?\d*)')[0].str.replace(" ", "").astype(float)
18print(df)
19# price price_float
20# 0 1.1円 1.1
21# 1 10.5円 10.5
22# 2 10円 10.0
23# 3 -10.0円 -10.0
24# 4 - 10円 -10.0
extractで抽出した結果はDataFrameとして返ってくる。この時、列名は0, 1, …のようになる。
今回の場合、1列のみを対象としているので、extractで返ってくるDataFrameの列は0のみになる。
extractで返ってきた列に対してreplaceでスペースを削除する。
その後、floatに変換できる。
まとめ
1# -N.NNなどの数値を抽出
2df["price_float"] = df["price"].str.extract('(-?\d+\.?\d*)').astype(float)
3
4# - N.NNなどの数値を抽出
5df["price_float"] = df["price"].str.extract('(-?\s*\d+\.?\d*)')[0].str.replace(" ", "").astype(float)
6