PostgreSQLデータベースにデータを保存しています。 Python2.7を使用してこのデータをクエリし、Pandas DataFrameに変換しています。ただし、このデータフレームの最後の列には、値の辞書(またはリスト?)が含まれています。 DataFrameは次のようになります。
[1] df
Station ID Pollutants
8809 {"a": "46", "b": "3", "c": "12"}
8810 {"a": "36", "b": "5", "c": "8"}
8811 {"b": "2", "c": "7"}
8812 {"c": "11"}
8813 {"a": "82", "c": "15"}
DataFrameが次のようになるように、この列を個別の列に分割する必要があります。
[2] df2
Station ID a b c
8809 46 3 12
8810 36 5 8
8811 NaN 2 7
8812 NaN NaN 11
8813 82 NaN 15
私が抱えている大きな問題は、リストが同じ長さではないということです。ただし、すべてのリストには、同じ3つの値(a、b、c)しか含まれていません。そして、それらは常に同じ順序で表示されます(1番目、b 2番目、c 3番目)。
次のコードは、私が望むものを正確に機能させて返すために使用しました(df2)。
[3] df
[4] objs = [df, pandas.DataFrame(df['Pollutant Levels'].tolist()).iloc[:, :3]]
[5] df2 = pandas.concat(objs, axis=1).drop('Pollutant Levels', axis=1)
[6] print(df2)
先週このコードを実行していましたが、うまく機能していました。しかし今、私のコードは壊れており、[4]行からこのエラーを受け取ります:
IndexError: out-of-bounds on slice (end)
コードを変更しませんでしたが、現在エラーが発生しています。これは私の方法が堅牢または適切でないためだと思います。
このリストの列を別々の列に分割する方法に関する提案やガイダンスは大歓迎です!
編集:.tolist()および.applyメソッドは、1つのUnicode文字列であるため、私のコードでは機能していません。
#My data format
u{'a': '1', 'b': '2', 'c': '3'}
#and not
{u'a': '1', u'b': '2', u'c': '3'}
データはこの形式でpostgreSQLデータベースからインポートされます。この問題に関するヘルプやアイデアはありますか? Unicodeを変換する方法はありますか?
文字列を実際の辞書に変換するには、df['Pollutant Levels'].map(eval)
を実行できます。その後、以下のソリューションを使用して、辞書を別の列に変換できます。
小さな例を使用すると、.apply(pd.Series)
を使用できます。
In [2]: df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]})
In [3]: df
Out[3]:
a b
0 1 {u'c': 1}
1 2 {u'd': 3}
2 3 {u'c': 5, u'd': 6}
In [4]: df['b'].apply(pd.Series)
Out[4]:
c d
0 1.0 NaN
1 NaN 3.0
2 5.0 6.0
それを残りのデータフレームと組み合わせるには、他の列をconcat
して上記の結果を得ることができます。
In [7]: pd.concat([df.drop(['b'], axis=1), df['b'].apply(pd.Series)], axis=1)
Out[7]:
a c d
0 1 1.0 NaN
1 2 NaN 3.0
2 3 5.0 6.0
あなたのコードを使用して、これはiloc
部分を省いた場合にも機能します:
In [15]: pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1)
Out[15]:
a c d
0 1 1.0 NaN
1 2 NaN 3.0
2 3 5.0 6.0
これを試してください:SQLから返されたデータは、Dictに変換する必要がありますまたは"Pollutant Levels"
がPollutants'
になります
StationID Pollutants
0 8809 {"a":"46","b":"3","c":"12"}
1 8810 {"a":"36","b":"5","c":"8"}
2 8811 {"b":"2","c":"7"}
3 8812 {"c":"11"}
4 8813 {"a":"82","c":"15"}
df2["Pollutants"] = df2["Pollutants"].apply(lambda x : dict(eval(x)) )
df3 = df2["Pollutants"].apply(pd.Series )
a b c
0 46 3 12
1 36 5 8
2 NaN 2 7
3 NaN NaN 11
4 82 NaN 15
result = pd.concat([df, df3], axis=1).drop('Pollutants', axis=1)
result
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
Merlinの答えはより良く、非常に簡単ですが、ラムダ関数は必要ありません。辞書の評価は、以下に示すように、次の2つの方法のいずれかで安全に無視できます。
方法1:2つのステップ
# step 1: convert the `Pollutants` column to Pandas dataframe series
df_pol_ps = data_df['Pollutants'].apply(pd.Series)
df_pol_ps:
a b c
0 46 3 12
1 36 5 8
2 NaN 2 7
3 NaN NaN 11
4 82 NaN 15
# step 2: concat columns `a, b, c` and drop/remove the `Pollutants`
df_final = pd.concat([df, df_pol_ps], axis = 1).drop('Pollutants', axis = 1)
df_final:
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
方法2:上記の2つの手順は一度に組み合わせることができます。
df_final = pd.concat([df, df['Pollutants'].apply(pd.Series)], axis = 1).drop('Pollutants', axis = 1)
df_final:
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
質問はかなり古いものですが、答えを探してここに来ました。実際、json_normalize
を使用してこれを行うより良い(そしてより速い)方法があります:
import pandas as pd
from pandas.io.json import json_normalize
df2 = json_normalize(df['Pollutant Levels'])
これにより、高価な適用機能を回避できます...
join
は、pop
+ tolist
と共に使用できます。パフォーマンスは、concat
+ drop
を使用したtolist
に匹敵しますが、この構文がよりきれいになる場合があります。
res = df.join(pd.DataFrame(df.pop('b').tolist()))
他の方法によるベンチマーク:
df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]})
def joris1(df):
return pd.concat([df.drop('b', axis=1), df['b'].apply(pd.Series)], axis=1)
def joris2(df):
return pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1)
def jpp(df):
return df.join(pd.DataFrame(df.pop('b').tolist()))
df = pd.concat([df]*1000, ignore_index=True)
%timeit joris1(df.copy()) # 1.33 s per loop
%timeit joris2(df.copy()) # 7.42 ms per loop
%timeit jpp(df.copy()) # 7.68 ms per loop
「汚染物質」列を抽出する方法を強くお勧めします。
df_pollutants = pd.DataFrame(df['Pollutants'].values.tolist(), index=df.index)
それははるかに高速です
df_pollutants = df['Pollutants'].apply(pd.Series)
dfのサイズが巨大な場合。