3.7. 参考:日本語の取り扱いと正規表現#
3.7.1. 日本語を含むデータを可視化する方法#
日本語をすぐに表示させたい場合は、便利なモジュールが公開されているので、そちらを使うのが簡単です。
requirements.txt
を用いたパッケージ類のダウンロードが済んでいない場合は、次のcellの1行目のコメントアウトを外してモジュールをインストールしてから実行してください。
# !pip install japanize-matplotlib #モジュールのインストール
import japanize_matplotlib
import matplotlib.pyplot as plt
import pandas as pd
years = [1960, 1970, 1980, 1990, 2000, 2010, 2020]
life_expectancy= [67.6, 71.9, 76.0, 78.8, 81.0, 82.8, 84.6]
plt.plot(years, life_expectancy, color = 'blue', marker = 'o', linestyle = 'solid')
plt.title('平均寿命')
plt.ylabel('歳')
plt.show()
3.7.2. ファイル読み書きでのエンコーディングのエラーへの対応方法#
続いて日本語のテキストを含むファイルの読み書きについてのTipsを紹介します。
日本語テキストのファイルの読み書きがうまく行かない場合があるかもしれません。
例えば、以下のようなtext_df
という日本語テキストを含むテーブルデータがあったとします。
text = ['パイソン','1','分析', '科学',
'統計', '動的','あーる', '機械学習', '深層学習', '見識']
freq = [100, 40, 80, 21, 10, 3, 45, 79, 81, 3]
importance = [200, 180, 190, 50, 150, 30, 20, 40, 20, 90]
text_df = pd.DataFrame({'text':text, 'freq': freq, 'importance': importance})
text_df
text | freq | importance | |
---|---|---|---|
0 | パイソン | 100 | 200 |
1 | 1 | 40 | 180 |
2 | 分析 | 80 | 190 |
3 | 科学 | 21 | 50 |
4 | 統計 | 10 | 150 |
5 | 動的 | 3 | 30 |
6 | あーる | 45 | 20 |
7 | 機械学習 | 79 | 40 |
8 | 深層学習 | 81 | 20 |
9 | 見識 | 3 | 90 |
このデータを保存してみましょう。
ここでは、テストのためencodingはSHIFT-JIS
にしています。
text_df.to_csv('text_ja.csv', encoding='SHIFT-JIS')
このようなテーブルをエクセル等で作成すると環境によってencodingが異なりPythonでひらけない場合があります。
そのような場合は、エクセルデータ作成時に文字コードを指定したり、作成後に文字コードを変更したりすることで回避できます。
まずは、読み込みたいデータの文字コードは何かを把握するようにしましょう。
日本語ファイルで多く用いられている文字コードはShift-JIS
, CP932
, utf-8
などです。
例えば、pandasのread_csv
ではデフォルトではencoding=utf-8
でファイルを読もうとします。そこにShift-JIS
などエンコードされたファイルをそのまま読もうとするとエラーになるはずです。
text_df_r = pd.read_csv('text_ja.csv')
---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
Cell In[5], line 1
----> 1 text_df_r = pd.read_csv('text_ja.csv')
File /opt/anaconda3/lib/python3.11/site-packages/pandas/io/parsers/readers.py:948, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
935 kwds_defaults = _refine_defaults_read(
936 dialect,
937 delimiter,
(...)
944 dtype_backend=dtype_backend,
945 )
946 kwds.update(kwds_defaults)
--> 948 return _read(filepath_or_buffer, kwds)
File /opt/anaconda3/lib/python3.11/site-packages/pandas/io/parsers/readers.py:611, in _read(filepath_or_buffer, kwds)
608 _validate_names(kwds.get("names", None))
610 # Create the parser.
--> 611 parser = TextFileReader(filepath_or_buffer, **kwds)
613 if chunksize or iterator:
614 return parser
File /opt/anaconda3/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1448, in TextFileReader.__init__(self, f, engine, **kwds)
1445 self.options["has_index_names"] = kwds["has_index_names"]
1447 self.handles: IOHandles | None = None
-> 1448 self._engine = self._make_engine(f, self.engine)
File /opt/anaconda3/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1723, in TextFileReader._make_engine(self, f, engine)
1720 raise ValueError(msg)
1722 try:
-> 1723 return mapping[engine](f, **self.options)
1724 except Exception:
1725 if self.handles is not None:
File /opt/anaconda3/lib/python3.11/site-packages/pandas/io/parsers/c_parser_wrapper.py:93, in CParserWrapper.__init__(self, src, **kwds)
90 if kwds["dtype_backend"] == "pyarrow":
91 # Fail here loudly instead of in cython after reading
92 import_optional_dependency("pyarrow")
---> 93 self._reader = parsers.TextReader(src, **kwds)
95 self.unnamed_cols = self._reader.unnamed_cols
97 # error: Cannot determine type of 'names'
File parsers.pyx:579, in pandas._libs.parsers.TextReader.__cinit__()
File parsers.pyx:668, in pandas._libs.parsers.TextReader._get_header()
File parsers.pyx:879, in pandas._libs.parsers.TextReader._tokenize_rows()
File parsers.pyx:890, in pandas._libs.parsers.TextReader._check_tokenize_status()
File parsers.pyx:2050, in pandas._libs.parsers.raise_parser_error()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 24: invalid start byte
UnicodeDecodeError
が確認されました。
このような場合はencodingを指定して読み込んでみましょう。encodingが何かを確認してから指定しましょう。
text_df_r = pd.read_csv('text_ja.csv', index_col = 0, encoding = 'cp932')
text_df_r
text | freq | importance | |
---|---|---|---|
0 | パイソン | 100 | 200 |
1 | 1 | 40 | 180 |
2 | 分析 | 80 | 190 |
3 | 科学 | 21 | 50 |
4 | 統計 | 10 | 150 |
5 | 動的 | 3 | 30 |
6 | あーる | 45 | 20 |
7 | 機械学習 | 79 | 40 |
8 | 深層学習 | 81 | 20 |
9 | 見識 | 3 | 90 |
無事に読み込めていることが確認されました。
3.7.3. 正規表現(regular expression)#
正規表現は文字列などを扱う上でとても便利です。ここでは、その基本的な使い方を紹介します。
ここでは正規表現の初歩とPythonでの扱い方を紹介します。
Pythonでの正規表現
普通の文字は全て正規表現です。
- a
という正規表現はa
にマッチします。
正規表現の初歩(の初歩)
.
(ドット)は任意の一文字にマッチ[]
で囲むと[]
の中の文字のいずれかにマッチ[AB]C
はAB
もしくはAC
のいずれかにマッチ[123]0
は10
,20
,30
のいずれかにマッチ全ての大文字アルファベットのいずれかにマッチは
[A-Z]
全ての整数値のいずれかにマッチは
[0-9]
*
は直前の文字の0回以上の繰り返し(0回 == なくても良い)AKB[0-9]*
はAKB49
にもAKB
にもAKB1
にもマッチ
(|)
はor(または)という意味になる(A|B)
は A or B(AまたはB)にマッチ
ソフトなどによって微妙に記法など異なる場合もある。
正規表現は奥が深い…
深く学びたい人は「詳説 正規表現」by Jeffrey Fried をどうぞ。
import re # Pythonで正規表現を扱うモジュール
# まずは、正規表現パターンを「正規表現オブジェクト」にコンパイルし、
# match()やsearch()などのメソッドを使ってマッチングに使えるようにします。
pat1 = re.compile('.')
pat2 = re.compile('[AB]')
pat3 = re.compile('[123]0')
pat4 = re.compile('AKB[0-9]*')
pat5 = re.compile('(A|B)')
# match()は文字列の先頭で0個以上の文字が正規表現パターンにマッチすれば対応するマッチオブジェクトを返します。
# 文字列がパターンにマッチしなければ None を返します
print(pat1.match('a') != None) # マッチすればTrueを返す
print(pat2.match('A') != None)
print(pat3.match('20') != None)
print(pat4.match('AKB49') != None)
print(pat5.match('A') != None)
True
True
True
True
True
# search()は文字列を走査し、正規表現パターンがマッチを生じさせる最初の場所を探して、対応するマッチオブジェクトを返します
print(pat1.search('a'))
print(pat2.search('A'))
print(pat3.search('20'))
print(pat4.search('AKB49'))
print(pat5.search('A'))
<re.Match object; span=(0, 1), match='a'>
<re.Match object; span=(0, 1), match='A'>
<re.Match object; span=(0, 2), match='20'>
<re.Match object; span=(0, 5), match='AKB49'>
<re.Match object; span=(0, 1), match='A'>
# 文字列の置き換えはre.sub
その他文字列の処理でよく使うもの
split() 特定の文字列で文字列を分割
startswith() 特定の文字列で文字列が始まるかを確認
endswith() 特定の文字列で文字列が終わるかを確認
strip() 文字列の先頭や末尾の特定の文字列や空白文字(スペース、タブ、改行)を削除
replace() 文字列の置き換え
text1 = 'I am happy to hear it.'
text1.split( ) # spaceで文字列を区切る
['I', 'am', 'happy', 'to', 'hear', 'it.']
text2 = '@hogehoge Count me in!'
text2.startswith('@') # ’@’で始まるか確認
True
file1 = 'test_code.ipynb'
file1.endswith('.ipynb') # '.ipynb'で終わるか確認
True
text3 = ' We are able to attend a class. '
text3.strip() # 文字列と最初の最後の空白文字を削除
'We are able to attend a class.'
text4 = "< aaaaabbbbbbcccccc >"
text4.strip("(<|>)") # 文字列と最初の最後の<または>を削除
' aaaaabbbbbbcccccc '
text5 = 'Hi Max.'
text5.replace('Max', 'Matrix') # Max を Matrixに置き換える
'Hi Matrix.'