【Python】Pandasでデータを抽出する時、整数値が小数点になってしまう問題の解決方法3選

# 概要
とある大規模CSVデータから、特定のヘッダーの値を指定し、その値の行にある値を取得するサンプルコードを作成しました。Pandasを利用する際、整数値が自動で小数点表示になってしまう問題にも遭遇したのでその原因と解決方法も記します。

# 前提とサンプルコード解説
以下は、果物コードを参考にして果物の数量と価格の値を抽出するコードです。
CSVファイルに果物コード(`FRUIT_CODE`)、数量(`QUANTITY`)、価格(`SALES_AMOUNT`)のヘッダーと値がそれぞれある前提です。

```py
import pandas as pd
import os
# CSVファイルのパス
csv_file_path = '/Users/xxx/Desktop/fruit_project/fruit_data.csv'
csv_encoding = "cp932"  # エンコーディングを指定

# ヘッダーのリストを指定
headers = ['QUANTITY','SALES_AMOUNT']

# FRUIT_CODEの値を指定
target_fruit_codes =  ['001', '002', '003']

# CSVファイルを読み込む
all_data = pd.read_csv(csv_file_path, encoding=csv_encoding)

# 各target_fruit_codeに対して処理を実行
for target_fruit_code in target_fruit_codes:
    # FRUIT_CODEが指定した値と一致する行を抽出
    filtered_data = all_data[all_data["FRUIT_CODE"] == target_fruit_code]

    # 指定したヘッダーの値を抽出
    selected_columns = filtered_data[headers]

    # 整数値の列を整数型に変換する
    for column in selected_columns.columns:
        if selected_columns[column].dtype == "float64":
            selected_columns[column] = selected_columns[column].astype(pd.Int64Dtype(), errors='ignore')

    # 結果をCSVファイルに書き込む
    output_file_name = f"{target_fruit_code}_output.csv"
    output_file_path = os.path.join(''/Users/xxx/Desktop/fruit_project/output', output_file_name)  # 出力ファイルのパスを指定
    selected_columns.to_csv(output_file_path, index=False)

    print(f"{output_file_name} の作成が完了しました。")
```



## 補足
`filtered_data`には、`all_data`DataFrame(=`pd.read_csv()`関数を使用して読み込んだCSV)から`"FRUIT_CODE"`列が`target_fruit_code`と一致する行が抽出されます。
`selected_columns`には、`filtered_data`から`headers`で指定された列だけが抽出されます。ここで、他に`COLOR`や`SIZE`などのヘッダーを加えたい場合は追記していきます。

## 整数値が小数点表示される問題（`1`が`1.0`となる件）
Pandas利用する際、数値データを取り扱うとfloatになることがあります。
Pandasのデータフレームでは、浮動小数点数（float64）は整数値（int64）よりも広い範囲の値を表現できるため、整数の列が少しでも欠損値（NaN）を含むと、その列全体が浮動小数点数（float64）に変換されるとのこと。つまり、NaNが一つでも含まれていると自動的にfloat型になるということ。
※NaNは非数（数ではない値） を表現するもの。Pythonにおいても、NaNはfloat型にのみ存在する概念となっています。
参考：[pandasのDataFrameで整数型に欠損値を追加したくて〜2022年冬〜](https://dev.classmethod.jp/articles/add-na-on-integer-from-pandas/)

そのため、`1`とCSVで入力されているデータが`1.0`と表示されることがありました。
これはどうもみづらい。みづらいというか、この値を取得してプログラムに連携させる場合、これのせいで期待しないエラーが発生する可能性すらあります。

そのため、`1.0`ではなく`1`として抽出する必要があります。

## 解決方法1：`pd.Int64Dtype()`を使って整数型に変換
小数点を落とすなどの処理を加えると他の小数点データにも影響が出てしまいます。ということで、自分はfloat64型の場合は、というIF文で整数型に変換する処理を追加しました。

```py
selected_columns[column].astype(pd.Int64Dtype(), errors='ignore')
```

上記により、DataFrameの特定の列を整数型（Int64）に変換します。
`astype()`メソッドは、指定されたデータ型に列のデータ型を変換し、`pd.Int64Dtype()`はpandasで提供される整数型のデータ型を表しています。
また、`errors='ignore'`オプションにより、変換中に発生したエラーを無視し、元のデータをそのまま保持します。これにより、整数型に変換できない（例：浮動小数点数の列に非整数値が含まれている）場合でも処理が中断されず、元のデータが保持されることになります。このオプションをつけないと以下のエラーが表示されます。

```
TypeError: cannot safely cast non-equivalent object to int64
```

これが解決方法1です。

### `lambda`を使う方法もあるが...
ちなみに、以下でも同じ処理が可能でした。

```py
selected_columns[column].apply(lambda x: int(x) if x % 1 == 0 else x)
```

このラムダ関数は、`x`が整数の場合(`if x % 1 == 0`)はそのまま整数に変換して返し、小数の場合(`else x`)は元の値`x`をそのまま返します。これによって、少数の値はそのまま少数として抽出され、整数のものは整数で返されます。
ただし、整数判定は、浮動小数点数が非常に小さな値（例:`1.0e-15`）でも`True`を返す可能性があるので、不正確な結果をもたらす可能性があるらしいです。`pd.Int64Dtype()`を使用した方が確実でしょう。


## 解決方法2：書式指定子を使って変換する方法
書式指定子`float_format='%g'`によって浮動小数点数の値を自動的に指数表記に切り替える方法があります。`'%g'`は「一般的な書式」（general format）を意味し、数値の大きさに応じて適切な表示を自動的に選択します。

`float_format='%.9f'`の場合は、浮動小数点数を小数点以下9桁まで表示する書式指定子なので、小数値がある場合には小数点以下9桁まで表示し、それ以上の桁は切り捨てられます。

上記の対応で解決するという記事もあったため、それでやってみました。

[pandasの出力で、floatにすると、整数が「x.0」ってなるやつの対応方法](https://workmemo.techblog.jp/archives/37844331.html)

しかし、自分の場合はそれでは別の問題が発生してしまいました。
それは、`%g`書式指定子は、整数部分および小数部分の有効桁数がそれぞれ**最大で6桁に制限**されるという点。適切な桁数に自動的に切り捨てられてしまいます。データの中には6桁以上のものもあったので省略されて抽出されるのは避けたいです。これ知らずにやってしまうと不正確なデータになるので要注意。

> デフォルトは6で、整数部分の桁数によらず小数点以下が指定した桁数になる。

参考：[pandasの表示設定変更（小数点以下桁数、有効数字、最大行数・列数など）](https://workmemo.techblog.jp/archives/37844331.html)
参考：[初心者のためのpandas基礎⑧桁数処理](https://qiita.com/Yanagawa_Yoshihisa/items/295a2184a48955eee45f)

では`%.9f`のようにして小数点以下を指定すれば？と思いましたが、これだと`41.2`のような数値の場合は`41.200000000`となってしまい、これもこれで正解と言えません。結果、書式指定子を使って小数点以下をいじるのではなく、上記の解決方法1を使った方法となりました。

## 解決方法3：`astype(int)`を使って整数に変換する方法
以下のように、`.astype(int)`を使って整数に変える方法もあります。

> pandas の read_csv で csv ファイルのデータを読みこむと、場合によって数値が浮動小数点数と扱われる。これを整数にするには
df['人口'] = df['人口'].astype(int)
とする。ポイントは .astype(int) であり、整数にしたいカラムにこれを指定するだけで解決する。

参考：[pandasのDataFrameで特定の列だけ浮動小数点数から整数に変える：.astype(int)を使う](https://www.irohabook.com/pandas-astype)

ただし、小数点のデータも整数値になってしまいます。
整数の値しかないという場合、もしくは全て整数値にしてしまいたい場合は、`astype(int)`の方法で良いと思いました。