pandasデータフレームの個々の行を操作していますが、行のインデックス作成と挿入中に強制の問題に遭遇しています。Pandasは常にint/floatの混合型からall-float型に強制変換します。この動作に対する明らかなコントロールはありません。
たとえば、以下はa
をint
として、b
をfloat
として含む単純なデータフレームです。
import pandas as pd
pd.__version__ # '0.25.2'
df = pd.DataFrame({'a': [1], 'b': [2.2]})
print(df)
# a b
# 0 1 2.2
print(df.dtypes)
# a int64
# b float64
# dtype: object
1つの行にインデックスを付ける際の強制の問題を次に示します。
print(df.loc[0])
# a 1.0
# b 2.2
# Name: 0, dtype: float64
print(dict(df.loc[0]))
# {'a': 1.0, 'b': 2.2}
そして、1つの行を挿入する際の強制の問題を次に示します。
df.loc[1] = {'a': 5, 'b': 4.4}
print(df)
# a b
# 0 1.0 2.2
# 1 5.0 4.4
print(df.dtypes)
# a float64
# b float64
# dtype: object
どちらの場合でも、a
列を、float型に強制的に変換するのではなく、整数型のままにしておきます。
少し掘り下げた後、ここにいくつかのひどく醜い回避策があります。 (より良い答えが受け入れられます。)
奇妙な ここにあります は、非数値列が強制を停止するということなので、1行をdict
にインデックスする方法は次のとおりです。
dict(df.assign(_='').loc[0].drop('_', axis=0))
# {'a': 1, 'b': 2.2}
行を挿入するには、1つの行で新しいデータフレームを作成します。
df = df.append(pd.DataFrame({'a': 5, 'b': 4.4}, index=[1]))
print(df)
# a b
# 0 1 2.2
# 1 5 4.4
これらのトリックはどちらも大きなデータフレーム用に最適化されていないため、より適切な回答をいただければ幸いです。
データフレームからデータを取得するか、データフレームにデータを追加し、データ型を同じに保つ必要がある場合は、必要なデータ型を認識しない他の内部構造への変換を避けてください。
df.loc[0]
を実行すると、 pd.Series
に変換され、
>>> type(df.loc[0])
<class 'pandas.core.series.Series'>
そして今、Series
にはdtype
が1つだけ含まれます。したがって、int
をfloat
に強制変換します。
代わりに、構造を pd.DataFrame
として保持します。
>>> type(df.loc[[0]])
<class 'pandas.core.frame.DataFrame'>
フレームとして必要な行を選択して、dict
に変換します
>>> df.loc[[0]].to_dict(orient='records')
[{'a': 1, 'b': 2.2}]
同様に、新しい行を追加するには、pandas pd.DataFrame.append
関数を使用します。
>>> df = df.append([{'a': 5, 'b': 4.4}]) # NOTE: To append as a row, use []
a b
0 1 2.2
0 5 4.4
上記は型変換を引き起こしません、
>>> df.dtypes
a int64
b float64
dtype: object
問題の根本は
次のことがわかります。
type(df.loc[0])
# pandas.core.series.Series
また、シリーズに含めることができるdtypeは1つだけです。あなたの場合は、int64またはfloat64です。
私の頭には2つの回避策があります。
print(df.loc[[0]])
# this will return a dataframe instead of series
# so the result will be
# a b
# 0 1 2.2
# but the dictionary is hard to read
print(dict(df.loc[[0]]))
# {'a': 0 1
# Name: a, dtype: int64, 'b': 0 2.2
# Name: b, dtype: float64}
または
print(df.astype(object).loc[0])
# this will change the type of value to object first and then print
# so the result will be
# a 1
# b 2.2
# Name: 0, dtype: object
print(dict(df.astype(object).loc[0]))
# in this way the dictionary is as expected
# {'a': 1, 'b': 2.2}
https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L697
if isinstance(other, dict):
other = Series(other)
だからあなたのウォークアラウンドは実際にはしっかりしたものであるか、そうでなければ私たちはできる:
df.append(pd.Series({'a': 5, 'b': 4.4}, dtype=object, name=1))
# a b
# 0 1 2.2
# 1 5 4.4
わずかなデータ操作による別のアプローチ:
辞書(またはデータフレーム)のリストがあると仮定します
lod=[{'a': [1], 'b': [2.2]}, {'a': [5], 'b': [4.4]}]
ここで、各辞書は行を表します(2番目の辞書のリストに注意してください)。その後、次の方法でデータフレームを簡単に作成できます。
pd.concat([pd.DataFrame(dct) for dct in lod])
a b
0 1 2.2
0 5 4.4
列のタイプを維持します。 concat を参照してください
したがって、データフレームと辞書のリストがある場合は、単に使用することができます
pd.concat([df] + [pd.DataFrame(dct) for dct in lod])
最初のケースでは、 nullable integer data type を使用できます。シリーズの選択はfloat
に強制されず、値はobject
コンテナーに配置されます。次に、辞書が適切に作成され、基礎となる値がnp.int64
として保存されます。
df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')
d = dict(df.loc[0])
#{'a': 1, 'b': 2.2}
type(d['a'])
#numpy.int64
あなたの構文では、これはalmostも2番目のケースで機能しますが、これはobject
にアップキャストするので、すばらしいことではありません。
df.loc[1] = {'a': 5, 'b': 4.4}
# a b
#0 1 2.2
#1 5 4.4
df.dtypes
#a object
#b float64
#dtype: object
ただし、最後に行を追加するための構文(RangeIndexを使用)に小さな変更を加えることができ、タイプが適切に処理されるようになりました。
df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')
df.loc[df.shape[0], :] = [5, 4.4]
# a b
#0 1 2.2
#1 5 4.4
df.dtypes
#a Int64
#b float64
#dtype: object