このようにpandasを cp932
や shift-jis
でファイルに吐き出そうとすると、
df.to_csv('tmp.csv', encoding='cp932')
こういったエラーが生じるケースがあります。
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-8-c349adf3c121> in <cell line: 1>()
----> 1 df.to_csv('tmp.csv', encoding='cp932')
5 frames
/usr/local/lib/python3.10/dist-packages/pandas/io/formats/csvs.py in _save_chunk(self, start_i, end_i)
318
319 ix = self.data_index[slicer]._format_native_types(**self._number_format)
--> 320 libwriters.write_csv_rows(
321 data,
322 ix,
writers.pyx in pandas._libs.writers.write_csv_rows()
UnicodeEncodeError: 'cp932' codec can't encode character '\u525d' in position 30: illegal multibyte sequence
文字コードの問題なのですが、対処法として、
-
errors='ignore'
を指定する -
encoding='utf8'
にする - 原因となってる文字を問題とならない文字に置き換える
などがあります。諸般の事情から3つ目をやりたいときが稀にありますが、どの文字が置き換えるべき対象なのか知るのが面倒です。それ用のコードを備忘としてメモしておきます。
1. どこの行がダメか教えてくれるやつ
import pandas as pd
def find_unencodable_characters(
df: pd.DataFrame, columns: list = None, encoding: str = 'cp932'
) -> pd.DataFrame:
"""
指定されたエンコーディングでエンコードできない文字をDataFrame内で検索する。
Args:
df (pd.DataFrame): エンコード可能性をチェックする対象のDataFrame。
columns (list, optional): チェック対象のカラムのリスト。指定しない場合は、
すべてのカラムが対象となる。デフォルトはNone。
encoding (str, optional): チェックするエンコーディングの種類。デフォルトは
'cp932'。
Returns:
pd.DataFrame: エンコードできない文字が見つかった場合、その詳細を含む
DataFrameを返す。このDataFrameは、'Column', 'Row',
'Problematic Char', および 'Full Value' のカラムを持つ。
"""
if columns is None:
columns = df.columns # デフォルトで全てのカラムをチェック
unencodable_chars_list = []
# 指定されたカラムごとにエンコードできない文字をチェック
for column in columns:
for i, value in enumerate(df[column].astype(str)):
try:
# 指定されたエンコーディングで文字列をエンコードを試みる
value.encode(encoding)
except UnicodeEncodeError as e:
# エンコードに失敗した場合、問題のある文字をキャプチャ
problematic_char = value[e.start:e.end]
unencodable_chars_list.append({
'Column': column,
'Row': i,
'Problematic Char': problematic_char,
'Full Value': value
})
return pd.DataFrame(unencodable_chars_list)
実行例
※例文はとても適当なことを言ってます。
# 例として使用するDataFrame
df = pd.DataFrame({
"アドバイス": [
"""
新しい塗料を使用する際には、剝離が発生する可能性があるため、
事前に適切な表面処理や下地材の選定が重要です。
特に、黄芩から抽出された成分が塗料の原料として使用されており、
これにより、塗布面積m²あたりの耐久性が従来の製品よりも
向上していることが確認されています。
""",
"""
製品ラベルに✕のマークが付いている場合、それは特定の条件下では
使用が制限されている成分が含まれていることを意味します。
また、この薬には心臓の亢進を引き起こすリスクがあり、
一部の患者には使用後に瘙痒感が現れることがありますが、
通常は一時的な症状であることが多いです。
""",
],
"リアクション": [
"Müller教授がこれについての第一人者らしいので、
彼の2014年の論文を読んでみます。",
"ありがとうございます。よく分かりました😊"
]
})
# 該当する行・列の確認
unencodable_chars_df = find_unencodable_characters(df)
unencodable_chars_df
出力結果
ちなみに、各セルの1文字目しかヒットしないので、たとえば「瘙」とかはエラーの原因となることが分かりません。
2. どの文字がダメか教えてくれるやつ
def find_unencodable_characters_in_string(
input_string: str, encoding: str = 'cp932'
) -> list:
"""
指定されたエンコーディングでエンコードできない文字を文字列内で検索する。
Args:
input_string (str): チェック対象の文字列。
encoding (str, optional): チェックするエンコーディングの種類。デフォルトは
'cp932'。
Returns:
list: エンコードできない文字を含むリストを返す。
"""
unencodable_chars = []
unique_chars = set(input_string) # ユニークな文字を取得
for char in unique_chars:
try:
# 文字を指定したエンコーディングでエンコード
char.encode(encoding)
except UnicodeEncodeError:
# エンコードできない文字をリストに追加
unencodable_chars.append(char)
return unencodable_chars