パンダはデータフレームをタプルの配列に変換します


131

私はパンダを使用していくつかのデータを操作しましたが、今度はデータベースにバッチ保存を実行したいと思います。これには、データフレームをタプルの配列に変換する必要があり、各タプルはデータフレームの「行」に対応しています。

私のDataFrameは次のようになります:

In [182]: data_set
Out[182]: 
  index data_date   data_1  data_2
0  14303 2012-02-17  24.75   25.03 
1  12009 2012-02-16  25.00   25.07 
2  11830 2012-02-15  24.99   25.15 
3  6274  2012-02-14  24.68   25.05 
4  2302  2012-02-13  24.62   24.77 
5  14085 2012-02-10  24.38   24.61 

それを次のようなタプルの配列に変換したい:

[(datetime.date(2012,2,17),24.75,25.03),
(datetime.date(2012,2,16),25.00,25.07),
...etc. ]

これを効率的に行う方法について何か提案はありますか?


21
2017年以降にこの回答に来る人のために、以下の新しい慣用的な解決策があります。そのまま使用できますlist(df.itertuples(index=False, name=None))
Ted Petrou 2017年

3
私がこの質問に来たときに私が探している2つのこと:タプルのリスト- df.to_records(index=False)と辞書のリスト:df.to_dict('records')
Martin Thoma

@MartinThoma to_recordsとto_dict( 'records')の両方がデータ型を台無しにします。既知のバグですが、このソリューションの価値がありません...
Jochen

回答:


206

どうですか:

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.to_numpy()]

パンダが0.24未満の場合

tuples = [tuple(x) for x in subset.values]

2
を使用するには.itertuples、以下の@ksindiの回答を参照してください。これは、値を配列として取得してタプルに変換するよりも効率的です。
vy32 2017

1
少しきれいです:tuples = map(tuple、subset.values)
RufusVS

これは値を別の型にキャストできますよね?
AMC

160
list(data_set.itertuples(index=False))

17.1以降、上記は namedtuplesのリストを返します。

通常のタプルのリストが必要な場合はname=None、引数として渡します。

list(data_set.itertuples(index=False, name=None))

39
これは受け入れられた答えであるはずです(専用機能が存在するようになったため)。ところで、あなたは通常したい場合はtuple、あなたの中のS zip(代わりのイテレータnamedtuple、秒)を呼び出します:data_set.itertuples(index=False, name=None)
アクセル・

2
実際には、そうすべきではありません。itertuples遅いです。可能であれば避けてください。これらの場合、forループ(承認された回答を参照)は通常、高速です。
cs95

3
@coldspeedタプルへの変換は通常、ベクトル化された/ cython操作よりも遅いため、リンクされた質問から私が得た教訓は、itertuplesが遅いということです。質問がタプルへの変換を求めていることを考えると、受け入れられた答えの方が速いと思う理由はありますか?私が行った簡単なテストでは、itertuplesバージョンの方が速いことがわかりました。
TCプロクター

2
スピードテストの結果をこの回答
TCプロクター

1
@johnDangerこれは、Pythonのeval()およびglobals()の概念に似ています。誰もが彼らが存在することを知っています。また、これらの関数は不適切な形式と見なされるため、通常は使用しないでください。ここでの原理は似ており、パンダでiter *ファミリーを使用するケースはほとんどありません。これは間違いなくそれらの1つです。私はまだ別の方法(リストコンプやマップなど)を使用しますが、それは私です。
cs95


30

動機
多くのデータセットは、速度/効率に関心を持つ必要があるほど十分に大きいです。だから私はその精神でこの解決策を提供します。たまたま簡潔です。

比較のために、index列を削除しましょう

df = data_set.drop('index', 1)

ソリューション
私はの使用を提案しているだろうzipし、map

list(zip(*map(df.get, df)))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

列の特定のサブセットを処理したい場合も、たまたま柔軟です。すでに表示した列が必要なサブセットであると想定します。

list(zip(*map(df.get, ['data_date', 'data_1', 'data_2'])))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Quickerとは何ですか?

ターンの出力recordsが最も速く、その後漸近的に収束しzipmapiter_tuples

この投稿simple_benchmarksから入手したライブラリを使用します

from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

import pandas as pd
import numpy as np

def tuple_comp(df): return [tuple(x) for x in df.to_numpy()]
def iter_namedtuples(df): return list(df.itertuples(index=False))
def iter_tuples(df): return list(df.itertuples(index=False, name=None))
def records(df): return df.to_records(index=False).tolist()
def zipmap(df): return list(zip(*map(df.get, df)))

funcs = [tuple_comp, iter_namedtuples, iter_tuples, records, zipmap]
for func in funcs:
    b.add_function()(func)

def creator(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

@b.add_arguments('Rows in DataFrame')
def argument_provider():
    for n in (10 ** (np.arange(4, 11) / 2)).astype(int):
        yield n, creator(n)

r = b.run()

結果を確認する

r.to_pandas_dataframe().pipe(lambda d: d.div(d.min(1), 0))

        tuple_comp  iter_namedtuples  iter_tuples   records    zipmap
100       2.905662          6.626308     3.450741  1.469471  1.000000
316       4.612692          4.814433     2.375874  1.096352  1.000000
1000      6.513121          4.106426     1.958293  1.000000  1.316303
3162      8.446138          4.082161     1.808339  1.000000  1.533605
10000     8.424483          3.621461     1.651831  1.000000  1.558592
31622     7.813803          3.386592     1.586483  1.000000  1.515478
100000    7.050572          3.162426     1.499977  1.000000  1.480131

r.plot()

ここに画像の説明を入力してください


12

以下は、次のようにaを返すベクトル化されたアプローチです(代わりdata_setに定義されるデータフレームを想定してdfいます)。listtuples

>>> df.set_index(['data_date'])[['data_1', 'data_2']].to_records().tolist()

生成する:

[(datetime.datetime(2012, 2, 17, 0, 0), 24.75, 25.03),
 (datetime.datetime(2012, 2, 16, 0, 0), 25.0, 25.07),
 (datetime.datetime(2012, 2, 15, 0, 0), 24.99, 25.15),
 (datetime.datetime(2012, 2, 14, 0, 0), 24.68, 25.05),
 (datetime.datetime(2012, 2, 13, 0, 0), 24.62, 24.77),
 (datetime.datetime(2012, 2, 10, 0, 0), 24.38, 24.61)]

日時列をインデックス軸として設定するという考えは、データフレームに対して引数を使用することにより、Timestamp対応するdatetime.datetime同等の形式への値の変換を支援することです。convert_datetime64DF.to_recordsDateTimeIndex

これは、recarraylist使用して返すようにできるa を返します.tolist


ユースケースに応じたより一般的なソリューションは次のとおりです。

df.to_records().tolist()                              # Supply index=False to exclude index

10

最も効率的で簡単な方法:

list(data_set.to_records())

この呼び出しの前に、必要な列をフィルタリングできます。


1
'index = False'はto_records()への引数として与えられるべきだと思います。したがって、list(data_set.to_records(index = False))
user3415167

8

この回答は、まだ説明されていない回答を追加するものではありませんが、速度に関する結果をいくつか示します。これはコメントで出てきた質問を解決するべきだと思います。これらはすべて、これら3つの値に基づいてO(n)のように見えます。

TL; DRtuples = list(df.itertuples(index=False, name=None))tuples = list(zip(*[df[c].values.tolist() for c in df]))最速のために結ばれています。

私はここで3つの提案の結果について簡単な速度テストを行いました:

  1. @pirsquaredからのzip回答: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. @ wes-mckinneyからの受け入れられた回答: tuples = [tuple(x) for x in df.values]
  3. itertuplesは@ksindiから@Axel name=Noneからの提案で答えます。tuples = list(df.itertuples(index=False, name=None))
from numpy import random
import pandas as pd


def create_random_df(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

小さいサイズ:

df = create_random_df(10000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

与える:

1.66 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
15.5 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.74 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

大きい:

df = create_random_df(1000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

与える:

202 ms ± 5.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1.52 s ± 98.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
209 ms ± 11.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

私と同じくらいの忍耐力:

df = create_random_df(10000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

与える:

1.78 s ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
15.4 s ± 222 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.68 s ± 96.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

zipバージョンとitertuplesバージョンは、互いに信頼区間内にあります。彼らは内部で同じことをしているのではないかと思います。

これらの速度テストはおそらく無関係です。私のコンピュータのメモリの限界を押すと、膨大な時間をとらない、とあなたは本当に大規模なデータセット上でこれを行うべきではありません。これを行った後でこれらのタプルを操作すると、結局は非効率になります。コードの主要なボトルネックになる可能性は低いので、最も読みやすいと思うバージョンをそのまま使用してください。


古い投稿を更新しました。今しばらく使っ[*zip(*map(df.get, df))]ていました。とにかく、面白そうだと思った。
piRSquared

@piRSquaredおお。私はかなりのプロットが好きです。それはO(n)のように見えると思います。
TCプロクター

2
#try this one:

tuples = list(zip(data_set["data_date"], data_set["data_1"],data_set["data_2"]))
print (tuples)

2

データフレームリストをタプルのリストに変更します。

df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]})
print(df)
OUTPUT
   col1  col2
0     1     4
1     2     5
2     3     6

records = df.to_records(index=False)
result = list(records)
print(result)
OUTPUT
[(1, 4), (2, 5), (3, 6)]

1
回答としてコードだけを投稿するのではなく、コードの機能と質問の問題をどのように解決するかについても説明してください。説明付きの回答は通常、質が高く、賛成票を集める可能性が高くなります。
Mark Rotteveel

1

よりpythonicな方法:

df = data_set[['data_date', 'data_1', 'data_2']]
map(tuple,df.values)

もっとpythonicな方法:正反対です。map()悪名高いがPythonのようではない。
AMC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.