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

Posted

技术标签:

【中文标题】Pandas 将数据帧转换为元组数组【英文标题】:Pandas convert dataframe to array of tuples 【发布时间】:2012-04-03 05:55:03 【问题描述】:

我已经使用 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 

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

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

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

【问题讨论】:

对于那些在 2017+ 年获得这个答案的人,有一个 new idiomatic solution below。你可以使用list(df.itertuples(index=False, name=None)) 当我谈到这个问题时,我正在寻找两件事:元组列表 - df.to_records(index=False) 和字典列表:df.to_dict('records') @MartinThoma to_records 和 to_dict('records') 都搞砸了我的数据类型。已知的错误,但使这个解决方案毫无价值...... 【参考方案1】:

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

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)]

【讨论】:

请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常质量更高,更有可能吸引投票。【参考方案2】:

怎么样:

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

对于熊猫

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

【讨论】:

关于使用.itertuples,请参阅下面@ksindi 的回答,这比将值作为数组获取并将它们转换为元组更有效。 稍微干净一点的是:tuples=map(tuple,subset.values) 这可以将值转换为不同的类型,对吧?【参考方案3】:

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

为了比较,我们删除index

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

解决方案 我建议使用zipmap

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)]

什么是更快?

结果records 最快,随后是渐近收敛的zipmapiter_tuples

我将使用从 this post 获得的库 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()

【讨论】:

【参考方案4】:

最有效和最简单的方法:

list(data_set.to_records())

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

【讨论】:

我认为 'index=False' 应该作为 to_records() 的参数给出。因此,list(data_set.to_records(index=False))【参考方案5】:
list(data_set.itertuples(index=False))

从 17.1 开始,上述内容将返回 list of namedtuples。

如果您想要一个普通元组的列表,请将name=None 作为参数传递:

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

【讨论】:

这应该是接受的答案恕我直言(现在存在专用功能)。顺便说一句,如果你想在你的zip 迭代器中使用普通的tuples(而不是namedtuples),然后调用:data_set.itertuples(index=False, name=None) 其实不应该。 itertuples is slow。尽可能避免。 For loops (as shown the the accepted answer) is usually faster in these cases. @coldspeed 我从链接问题中得到的教训是 itertuples 很慢,因为转换为元组通常比向量化/cython 操作慢。鉴于问题是要求转换为元组,我们有什么理由认为接受的答案更快?我做的快速测试表明 itertuples 版本更快。 我在this answer发布了我的速度测试结果 @johnDanger 它类似于python中 eval() 和 globals() 的概念。每个人都知道他们的存在。每个人都知道你通常不应该使用这些函数,因为它被认为是不好的形式。这里的原理是类似的,在 pandas 中使用 iter* 家族的情况很少,可以说是其中之一。我仍然会使用不同的方法(如列表组合或地图),但这就是我。【参考方案6】:

这个答案没有添加任何尚未讨论的答案,但这里有一些速度结果。我认为这应该可以解决 cmets 中出现的问题。根据这三个值,所有这些看起来都是 O(n)

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

我在这里对三个建议的结果进行了快速测试:

    @pirsquared 的邮政编码:tuples = list(zip(*[df[c].values.tolist() for c in df])) @wes-mckinney 接受的答案:tuples = [tuple(x) for x in df.values] 来自@ksindi 的 itertuples 使用来自@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 哦。我喜欢漂亮的情节。我猜这看起来像是 O(n).【参考方案7】:
#try this one:

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

【讨论】:

【参考方案8】:

更多pythonic方式:

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

【讨论】:

更 Python 的方式: 实际上恰恰相反。 map() 是出了名的非 Python 语言。【参考方案9】:

这是一种矢量化方法(假设数据框 data_set 被定义为 df),它返回 tupleslist,如图所示:

>>> 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)]

将日期时间列设置为索引轴的想法是通过使用DF.to_records 中的convert_datetime64 参数来帮助将Timestamp 值转换为其对应的datetime.datetime 格式等效项,这样做是为了DateTimeIndex 数据框。

这会返回一个recarray,然后可以使用.tolist 使其返回一个list


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

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

【讨论】:

【参考方案10】:

通用方式:

[tuple(x) for x in data_set.to_records(index=False)]

【讨论】:

data_set.to_records(index=False).tolist() 不是更好吗?

以上是关于Pandas 将数据帧转换为元组数组的主要内容,如果未能解决你的问题,请参考以下文章

将火花数据帧动态转换为元组数据集(字符串,_<:产品)

Swift将数组转换为元组数组

将数据框转换为元组列表

将数据框转换为元组列表[重复]

将数据框转换为元组列表字典

如何创建将二叉树转换为元组的函数?