以下のように、.csvファイルからPandasデータフレームにデータを読み取ります。列の1つ、つまりid
に対して、列タイプをint
として指定します。問題は、id
シリーズの値が欠落しているか空になっていることです。
.csvの読み取り中にid
列を整数にキャストしようとすると、次のようになります。
df= pd.read_csv("data.csv", dtype={'id': int})
error: Integer column has NA values
または、以下のように読んだ後、列タイプを変換しようとしましたが、今回は次のようになります:
df= pd.read_csv("data.csv")
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer
どうすればこれに取り組むことができますか?
整数列にNaN担当者がいないのは pandas "gotcha" です。
通常の回避策は、単純にフロートを使用することです。
私のユースケースは、DBテーブルにロードする前にデータを変更することです。
df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)
NaNを削除し、intに変換し、strに変換してから、NANを再挿入します。
それはきれいではありませんが、仕事を終わらせます!
バージョン0.24。+では、pandasに、欠損値を持つ整数dtypeを保持する機能が追加されました。
パンダは arrays.IntegerArray
を使用して、おそらく欠損値のある整数データを表すことができます。これは、パンダ内で実装される拡張機能タイプです。これは整数のデフォルトのdtypeではなく、推測されません。 dtypeを array()
またはSeries
に明示的に渡す必要があります。
arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)
0 1
1 2
2 NaN
dtype: Int64
保存されたデータを変更できる場合は、欠落しているid
にセンチネル値を使用します。 id
が厳密にゼロより大きい整数であるという、列名によって推測される一般的な使用例では、0
を番兵値として使用して、
if row['id']:
regular_process(row)
else:
special_process(row)
pandas 0.24.0に正式に追加されたため、Natypeをdtype int
として含むpandas列を作成できるようになりました。
pandas 0.24.xリリースノート 引用: "Pandasは、欠損値を持つ整数dtypeを保持する機能を獲得しました
NaN値を含む行を削除してもよい場合は、.dropna()
を使用できます。
df = df.dropna(subset=['id'])
または、.fillna()
と.astype()
を使用して、NaNを値に置き換え、それらをintに変換します。
大きな整数を持つCSVファイルを処理しているときにこの問題に遭遇しましたが、それらの一部は欠落していました(NaN)。型としてfloatを使用することはオプションではありません。精度を失う可能性があるからです。
私の解決策は中間型としてstrを使用でした。その後、後でコードで必要に応じて文字列をintに変換できます。 NaNを0に置き換えましたが、任意の値を選択できます。
df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)
説明のために、フロートが精度を失う可能性がある方法の例を次に示します。
s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)
出力は次のとおりです。
1.2345678901234567e+19 12345678901234567168 12345678901234567890
列内で整数とNaNを絶対に組み合わせたい場合は、「オブジェクト」データ型を使用できます。
df['col'] = (
df['col'].fillna(0)
.astype(int)
.astype(object)
.where(df['col'].notnull())
)
これにより、NaNが整数に置き換えられ(どちらでもかまいません)、intに変換され、オブジェクトに変換され、最後にNaNが再挿入されます。
ここでのほとんどのソリューションは、プレースホルダー整数を使用してヌルを表す方法を示しています。ただし、整数がソースデータに表示されないことが確実でない場合、この方法は役に立ちません。を使用する私のメソッドは、小数値を持たない浮動小数点をフォーマットし、nullをNoneに変換します。結果は、CSVにロードされたときにNULL値を持つ整数フィールドのように見えるオブジェクトデータ型です。
keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))
私はpysparkでこの問題に遭遇しました。これはjvm上で実行されるコードのpythonフロントエンドであるため、タイプセーフが必要であり、intの代わりにfloatを使用することはオプションではありません。 pandas pd.read_csv
を、必要な型にキャストする前にユーザー定義の列をユーザー定義の塗りつぶし値で埋める関数でラップすることで、この問題を回避しました。ここに私が使用したものがあります:
def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
if custom_dtype is None:
return pd.read_csv(file_path, **kwargs)
else:
assert 'dtype' not in kwargs.keys()
df = pd.read_csv(file_path, dtype = {}, **kwargs)
for col, typ in custom_dtype.items():
if fill_values is None or col not in fill_values.keys():
fill_val = -1
else:
fill_val = fill_values[col]
df[col] = df[col].fillna(fill_val).astype(typ)
return df
3312018.0でフォーマットされたDateColumnを文字列として03/31/2018に変換する必要があると仮定します。また、一部のレコードが欠落しているか、0です。
df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
最初に、NaNを含む行を削除します。次に、残りの行で整数変換を行います。最後に、削除された行を再度挿入します。うまくいくことを願って