Pandas将数据帧转换为元组数组

enr*_*shi 106 python pandas

我已经使用pandas操纵了一些数据,现在我想执行批量保存回数据库.这需要我将数据帧转换为元组数组,每个元组对应于数据帧的"行".

我的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 
Run Code Online (Sandbox Code Playgroud)

我想将它转换为元组数组,如:

[(datetime.date(2012,2,17),24.75,25.03),
(datetime.date(2012,2,16),25.00,25.07),
...etc. ]
Run Code Online (Sandbox Code Playgroud)

关于如何有效地做到这一点的任何建议?

Wes*_*ney 176

怎么样:

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.values]
Run Code Online (Sandbox Code Playgroud)

  • 请参阅下面@ksindi 的回答以使用`.itertuples`,这比将值作为数组获取并将它们转换为元组更有效。 (5认同)
  • 稍微干净一点的是: tuples=map(tuple,subset.values) (3认同)

ksi*_*ndi 117

list(data_set.itertuples(index=False))
Run Code Online (Sandbox Code Playgroud)

从17.1开始,上面将返回一个namedtuples列表 - 请参阅文档.

  • 这应该是公认的答案恕我直言(现在存在专用功能).顺便说一句,如果你想在你的`zip`迭代器(而不是`namedtuple`s)中使用正常的`tuple`,那么调用:`data_set.itertuples(index = False,name = None)` (34认同)
  • 实际上,不应该。[`itertuples`很慢](/sf/ask/1740966741/)。尽可能避免。[在这些情况下,循环(通常显示为可接受的答案)通常更快。](/sf/ask/3781973961/) (2认同)
  • @coldspeed我从链接的问题中学到的一点是,itertuples很慢,因为转换为元组通常比矢量化/ cython操作要慢。鉴于问题要求转换为元组,是否有任何理由使我们认为接受的答案更快?我所做的快速测试表明itertuples版本更快。 (2认同)
  • 我在 [this answer](/sf/answers/3846656061/) 中发布了我的速度测试结果 (2认同)

Ram*_*gil 43

通用方式:

[tuple(x) for x in data_set.to_records(index=False)]
Run Code Online (Sandbox Code Playgroud)

  • `data_set.to_records(index=False).tolist()` 不是更好吗? (3认同)

piR*_*red 19

动机
许多数据集足够大,我们需要关注速度/效率.所以我本着这种精神提供这种解决方案.它恰好也很简洁.

为了便于比较,让我们放弃index

df = data_set.drop('index', 1)
Run Code Online (Sandbox Code Playgroud)

解决方案
我将提出使用zip和理解

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)]
Run Code Online (Sandbox Code Playgroud)

如果我们想处理特定的列子集,它恰好也是灵活的.我们假设我们已经显示的列是我们想要的子集.

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)]
Run Code Online (Sandbox Code Playgroud)

以下所有产生相同的结果

  • map
  • records
  • zipmap
  • iter_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()
Run Code Online (Sandbox Code Playgroud)

小数据

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
Run Code Online (Sandbox Code Playgroud)

大数据

r.plot()
Run Code Online (Sandbox Code Playgroud)


Gow*_*amy 14

将数据框列表更改为元组列表。

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)]
Run Code Online (Sandbox Code Playgroud)

  • 请不要仅发布代码作为答案,还要提供解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常质量更高,并且更有可能吸引点赞。 (2认同)

Nic*_*eli 11

这里的一个矢量化的方法(假设数据帧,data_set将被定义df返回一个代替)listtuples,如下所示:

>>> df.set_index(['data_date'])[['data_1', 'data_2']].to_records().tolist()
Run Code Online (Sandbox Code Playgroud)

生产:

[(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)]
Run Code Online (Sandbox Code Playgroud)

将datetime列设置为索引轴的想法是通过利用对数据帧执行此操作的参数来帮助将Timestamp值转换为相应的datetime.datetime格式.convert_datetime64DF.to_recordsDateTimeIndex

这将返回一个recarray然后可以返回list使用.tolist


根据用例,更通用的解决方案是:

df.to_records().tolist()                              # Supply index=False to exclude index
Run Code Online (Sandbox Code Playgroud)


T.C*_*tor 6

该答案不会添加尚未讨论的任何答案,但是这里提供了一些速度结果。我认为这应该可以解决评论中出现的问题。基于这三个值,所有这些看起来都像是O(n)

TL; DRtuples = list(df.itertuples(index=False, name=None))tuples = list(zip(*[df[c].values.tolist() for c in df]))并列最快。

我对结果进行了快速速度测试,其中包含三个建议:

  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)})
Run Code Online (Sandbox Code Playgroud)

小尺寸:

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))
Run Code Online (Sandbox Code Playgroud)

给出:

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)
Run Code Online (Sandbox Code Playgroud)

较大:

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))
Run Code Online (Sandbox Code Playgroud)

给出:

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)
Run Code Online (Sandbox Code Playgroud)

尽我所能:

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))
Run Code Online (Sandbox Code Playgroud)

给出:

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)
Run Code Online (Sandbox Code Playgroud)

zip版本和itertuples版本彼此在置信区间内。我怀疑他们在幕后做着同样的事情。

这些速度测试可能无关紧要。突破计算机内存的限制并不需要花费大量时间,并且您实际上不应该对大型数据集执行此操作。完成这些操作后,使用这些元组将最终效率低下。它不太可能成为代码中的主要瓶颈,因此,请坚持使用您认为最易读的版本。


Gus*_*ves 5

最有效,最简单的方法:

list(data_set.to_records())
Run Code Online (Sandbox Code Playgroud)

您可以在此调用之前过滤所需的列。

  • 我认为“index=False”应该作为 to_records() 的参数给出。因此, list(data_set.to_records(index=False)) (3认同)