[解決済み] pandas DataFrameのカラムを複数行にアンネスト(分解)する方法
質問
以下のようなDataFrameがあり、列の1つがオブジェクト(リスト型セル)になっています。
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[1,2]]})
df
Out[458]:
A B
0 1 [1, 2]
1 2 [1, 2]
私が期待する出力は
A B
0 1 1
1 1 2
3 2 1
4 2 2
そのためにはどうしたらいいのでしょうか?
関連する質問
pandasです。セルの内容がリストの場合、リスト内の各要素に対して行を作成する
良い質問と回答ですが、リストで1列しか扱えません(私の回答では、自己定義関数は複数の列に対して機能します。
apply
これは推奨されませんので、詳細を確認してください。
自分のコードでpandas apply()を使いたいのはどんなとき?
)
どのように解決するのですか?
知っている
object
d型カラムの場合、pandasの関数でデータを変換するのが難しくなります。このようなデータを受け取ったとき、最初に思いついたのは "flatten" または列の非正規化でした。
このような質問にはpandasとPythonの関数を使用しています。もし、上記のソリューションの速度が心配なら、以下をチェックしてみてください。 user3483203さんの回答 これはnumpyを使用しており、ほとんどの場合numpyの方が速いからです。私のお勧めは Cython または ヌンバ 速度が重要な場合は
メソッド0 [pandas >= 0.25]です。
出発地
パンダ0.25
を爆発させればよいのであれば
一
カラムを使用する場合は
pandas.DataFrame.explode
関数を使用します。
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
データフレームが空の場合
list
または
NaN
を列挙してください。空のリストでは問題が発生しませんが
NaN
で埋める必要があります。
list
df = pd.DataFrame({'A': [1, 2, 3, 4],'B': [[1, 2], [1, 2], [], np.nan]})
df.B = df.B.fillna({i: [] for i in df.index}) # replace NaN with []
df.explode('B')
A B
0 1 1
0 1 2
1 2 1
1 2 2
2 3 NaN
3 4 NaN
方法1
apply + pd.Series
(理解しやすいが、パフォーマンス的にはお勧めできない。)
df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
Out[463]:
A B
0 1 1
1 1 2
0 2 1
1 2 2
方法2
使用方法
repeat
と
DataFrame
コンストラクタで、データフレームを再作成します。
df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
df
Out[465]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
方法2.1 たとえば、A のほかに A.1 ......A.n がある場合、このメソッド( 方法2 )上の列を1つずつ再作成するのは大変です。
解決策:
join
または
merge
と共に
index
単一カラムを「アンネスト」した後
s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
s.join(df.drop('B',1),how='left')
Out[477]:
B A
0 1 1
0 2 1
1 1 2
1 2 2
もし、以前と全く同じカラムオーダーが必要な場合は、以下のようにします。
reindex
を末尾に追加してください。
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
方法3
を再作成します。
list
pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
Out[488]:
A B
0 1 1
1 1 2
2 2 1
3 2 2
2列以上の場合は
s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
s.merge(df,left_on=0,right_index=True)
Out[491]:
0 1 A B
0 0 1 1 [1, 2]
1 0 2 1 [1, 2]
2 1 1 2 [1, 2]
3 1 2 2 [1, 2]
方法4
使用
reindex
または
loc
df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
Out[554]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
#df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
方法5 リストが一意な値のみを含む場合。
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
from collections import ChainMap
d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
pd.DataFrame(list(d.items()),columns=df.columns[::-1])
Out[574]:
B A
0 1 1
1 2 1
2 3 2
3 4 2
方法6
使用
numpy
を使用することで、高いパフォーマンスを得ることができます。
newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
pd.DataFrame(data=newvalues[0],columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
メソッド7
基底関数による
itertools
cycle
と
chain
: 純粋な Python ソリューション。
from itertools import cycle,chain
l=df.values.tolist()
l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
複数カラムへの一般化
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
df
Out[592]:
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4]
Self-def機能。
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
unnesting(df,['B','C'])
Out[609]:
B C A
0 1 1 1
0 2 2 1
1 3 3 2
1 4 4 2
列単位のアンネスティング
上記の方法は、すべて
垂直
ネスティングやエクスプロードが必要な場合、リストを消費する必要があります。
水平
でチェックします。
pd.DataFrame
コンストラクタ
df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
Out[33]:
A B C B_0 B_1
0 1 [1, 2] [1, 2] 1 2
1 2 [3, 4] [3, 4] 3 4
更新された機能
def unnesting(df, explode, axis):
if axis==1:
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
テスト出力
unnesting(df, ['B','C'], axis=0)
Out[36]:
B0 B1 C0 C1 A
0 1 2 1 2 1
1 3 4 3 4 2
2021-02-17更新 オリジナルexplode機能搭載
def unnesting(df, explode, axis):
if axis==1:
df1 = pd.concat([df[x].explode() for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
関連
-
[解決済み] PandasでDataFrameの行を反復処理する方法
-
[解決済み] 列の値に基づいてDataFrameから行を選択するにはどうすればよいですか?
-
[解決済み] Pandasのカラム名のリネーム
-
[解決済み] Pandas DataFrameからカラムを削除する
-
[解決済み] Pandasのデータフレームで複数の列を選択する
-
[解決済み] Pandas DataFrameの行数を取得する方法は?
-
[解決済み] 既存のDataFrameに新しい列を追加する方法は?
-
[解決済み] Pandas DataFrameの特定の列の値がNaNである行を削除する方法
-
[解決済み] Pandasのデータフレームから行を選択するために値のリストを使用する
-
[解決済み】Pandas DataFrameのカラムヘッダからリストを取得する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Python関数の高度な応用を解説
-
python string splicing.join()とsplitting.split()の説明
-
PicgoのイメージベッドツールをPythonで実装する
-
python implement mysql add delete check change サンプルコード
-
Pythonの学習とデータマイニングのために知っておくべきターミナルコマンドのトップ10
-
[解決済み】ImportError: sklearn.cross_validation という名前のモジュールがない。
-
[解決済み】DataFrameのコンストラクタが正しく呼び出されない!エラー
-
[解決済み】Python regex AttributeError: 'NoneType' オブジェクトに 'group' 属性がない。
-
[解決済み】Django: ImproperlyConfigured: SECRET_KEY 設定は空であってはならない
-
[解決済み】django インポートエラー - core.managementという名前のモジュールがない