0
1

pandasのto_csvでの文字化けエラーの原因を探す

Posted at

このようにpandasを cp932shift-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

文字コードの問題なのですが、対処法として、

  1. errors='ignore' を指定する
  2. encoding='utf8' にする
  3. 原因となってる文字を問題とならない文字に置き換える

などがあります。諸般の事情から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

出力結果

image.png

ちなみに、各セルの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

実行結果

image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1