パンダはリストの列を複数の列に分割します


136

私は1列のパンダデータフレームを持っています:

import pandas as pd

df = pd.DataFrame(
    data={
        "teams": [
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
        ]
    }
)

print(df)

出力:

       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

リストのこの列を2つの列に分割するにはどうすればよいですか?

回答:


243

あなたはcreated byでDataFrameコンストラクタを使うことができます:liststo_list

import pandas as pd

d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

df2[['team1','team2']] = pd.DataFrame(df2.teams.tolist(), index= df2.index)
print (df2)
       teams team1 team2
0  [SF, NYG]    SF   NYG
1  [SF, NYG]    SF   NYG
2  [SF, NYG]    SF   NYG
3  [SF, NYG]    SF   NYG
4  [SF, NYG]    SF   NYG
5  [SF, NYG]    SF   NYG
6  [SF, NYG]    SF   NYG

そして、新しいDataFrame

df3 = pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
print (df3)
  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

の解決apply(pd.Series)は非常に遅いです:

#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [121]: %timeit df2['teams'].apply(pd.Series)
1.79 s ± 52.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [122]: %timeit pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
1.63 ms ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

4
軽微な警告、既存のデータフレームで使用している場合は、必ずインデックスをリセットしてください。リセットしないと、正しく割り当てられません。
user1700890

1
@ user1700890-はい、またはDataFrameコンストラクターでインデックスを指定df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
jezrael

1
@Catbuilts-はい、ベクトル化ソリューションが存在する場合は回避するのが最善です。
jezrael

1
@Catbuilts-はい、明らかに。ベクトル化とは、一般にループがないことを意味するため、適用、適用、リストの理解はありません。しかし、それは何が必要かによります。多分これ
jezrael

2
@Catbuilts確かapply()に遅いかもしれませんが、元のSeriesの行間で入力文字列と値が等しくない場合の頼りになるメソッドです!
CheTesta

52

より簡単な解決策:

pd.DataFrame(df2["teams"].to_list(), columns=['team1', 'team2'])

収量、

  team1 team2
-------------
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
7    SF   NYG

リストではなく区切られた文字列の列を分割したい場合は、同様に行うことができます:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])

5
各リストの要素数が不均一な場合はどうなりますか?
-ikel、

リストではなく区切られた文字列の列を分割する場合も、同様に行うことができます。 df["teams"].str.split('<delim>', expand=True)すでにDataFrameを返しているので、列の名前を変更するだけの方がおそらく簡単です。
AMC

26

このソリューションは、df2を使用する他のソリューションとは異なり、DataFrame のインデックスを保持しますtolist()

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']

結果は次のとおりです。

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

2
またapply、パンダでできる最も遅いものの1つです。この方法は避け、受け入れられた回答を使用する必要があります。一番上の答えのタイミングでは、この方法は1400 x@rajan でおよそ遅くなります
Erfan

2
@Erfanはい、ただし、ユーザーは操作に1秒と1ミリ秒のどちらがかかるかを気にせず、代わりに最も単純で最も読みやすいコードの記述に最も関心があります。私は読みやすさ/シンプルさは主観的であることを認めますが、私のポイントは、速度がすべてのユーザーにとって常に優先事項ではないということです。
Kevin Markham

1
さらに、このapply方法は、大規模なデータセットで大規模な配列(1000以上のアイテム)を拡張する場合により確実に機能することがわかりました。tolist()データセットが500k行を超えると、このメソッドはプロセスを強制終了しました。
モリッツ

2
これは、さまざまなサイズのリストでうまく機能するため、優れたソリューションです。
dasilvadaniel

@KevinMarkham 彼らは最も簡単な、最も読みやすいコードを書くことについて最も気にされていますpd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])本当にそんなに多くは複雑?
AMC

15

提案されたソリューションとは対照的に、構文的には単純な方法があり、したがって覚えやすい方法のようです。私は、列がデータフレームdfで「メタ」と呼ばれていると仮定しています:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())

1
エラーが発生しましたが、を削除して解決しましたstr.split()。これははるかに単純で、リスト内のアイテム数がわからない場合に有利です。
otteheng 2018年

提案されたソリューションとは対照的に、構文的には単純な方法があり、したがって覚えやすい方法のようです。本当に?これは、数年前に投稿されたトップの回答と実質的に同じだからです。唯一の違いは、この特定の質問に関連しない部分です。
AMC

うまくいきます!
EduardoUstarez

3

以前の回答に基づいて、実行時間がはるかに速いdf2.teams.apply(pd.Series)と同じ結果を返す別のソリューションを次に示します。

pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

タイミング:

In [1]:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [2]: %timeit df2['teams'].apply(pd.Series)

8.27 s ± 2.73 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

35.4 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

3

上記の解決策は、私のでnan観察結果があったため機能しませんdataframe。私の場合、df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)収量:

object of type 'float' has no len()

私はリスト内包表記を使用してこれを解決します。ここに複製可能な例:

import pandas as pd
import numpy as np
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
            ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2.loc[2,'teams'] = np.nan
df2.loc[4,'teams'] = np.nan
df2

出力:

        teams
0   [SF, NYG]
1   [SF, NYG]
2   NaN
3   [SF, NYG]
4   NaN
5   [SF, NYG]
6   [SF, NYG]

df2['team1']=np.nan
df2['team2']=np.nan

リスト内包表記で解く:

for i in [0,1]:
    df2['team{}'.format(str(i+1))]=[k[i] if isinstance(k,list) else k for k in df2['teams']]

df2

収量:

    teams   team1   team2
0   [SF, NYG]   SF  NYG
1   [SF, NYG]   SF  NYG
2   NaN        NaN  NaN
3   [SF, NYG]   SF  NYG
4   NaN        NaN  NaN
5   [SF, NYG]   SF  NYG
6   [SF, NYG]   SF  NYG

1

リスト内包

リスト内包表記による簡単な実装(私のお気に入り)

df = pd.DataFrame([pd.Series(x) for x in df.teams])
df.columns = ['team_{}'.format(x+1) for x in df.columns]

出力のタイミング:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.71 ms

出力:

team_1  team_2
0   SF  NYG
1   SF  NYG
2   SF  NYG
3   SF  NYG
4   SF  NYG
5   SF  NYG
6   SF  NYG

この種の方法は、さまざまな長さのリストを処理します。これは、他の多くの回答よりも改善されていますが、アイテムが独自の列に含まれていません。
アイザック

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.