有效地将numpy数组转换为pandas系列数组

时间:2018-08-05 03:31:53

标签: python arrays pandas numpy

如何将numpy数组的numpy数组有效地转换为数组列表?最终,我想使pandas系列数组成为一个数据框。如果有一个更好的方法可以直接解决这个问题,那也很好。

以下可重现的代码解决了list().tolist()的问题,但是任何一个都太慢而无法在我的实际数据集上实现。我正在寻找更快的东西。

import numpy as np 
import pandas as pd

a = np.array([np.array([0,1,2,3]), np.array([4,5,6,7])])

s = pd.Series(a.tolist())

s = pd.Series(list(a))

这导致形状从a.shape = (2,4)变为s.values.shape = (2,)

2 个答案:

答案 0 :(得分:2)

您的a

In [2]: a = np.array([np.array([0,1,2,3]), np.array([4,5,6,7])])
   ...: 

a是(2,4)数字数组;我们可能只写了a = np.array([[0,1,2,3],[4,5,6,7]])。创建(2,)数组的数组需要不同的构造。

就像其他人所写的那样,制作一个数据框是微不足道的:

In [3]: pd.DataFrame(a)     # dtypes int64
Out[3]: 
   0  1  2  3
0  0  1  2  3
1  4  5  6  7

但是从中制作系列会引发错误:

In [4]: pd.Series(a)
---------------------------------------------------------------------------
...
Exception: Data must be 1-dimensional

如果它显示此错误,那么您的问题就更清楚了,为什么然后您尝试使用列表输入:

In [5]: pd.Series(a.tolist())
Out[5]: 
0    [0, 1, 2, 3]
1    [4, 5, 6, 7]
dtype: object
In [6]: pd.Series(list(a))
Out[6]: 
0    [0, 1, 2, 3]
1    [4, 5, 6, 7]
dtype: object

从表面上看,它们是相同的,但是当我们查看系列的实际元素时,我们看到一个包含列表,另一个包含列表。这是因为tolistlist()从数组中创建了不同的列表。

In [8]: Out[5][0]
Out[8]: [0, 1, 2, 3]
In [9]: Out[6][0]
Out[9]: array([0, 1, 2, 3])

我的经验是a.tolist()相当快。 list(a)等效于[i for i in a];实际上,它会在a的第一维上进行迭代,每次都返回(在这种情况下)一个1d数组(行)。


让我们更改a,使其成为一维对象dtype数组:

In [14]: a = np.array([np.array([0,1,2,3]), np.array([4,5,6,7]), np.array([1]), None])
In [15]: a
Out[15]: 
array([array([0, 1, 2, 3]), array([4, 5, 6, 7]), array([1]), None],
      dtype=object)

现在我们可以从中制作系列了:

In [16]: pd.Series(a)
Out[16]: 
0    [0, 1, 2, 3]
1    [4, 5, 6, 7]
2             [1]
3            None
dtype: object
In [17]: Out[16][0]
Out[17]: array([0, 1, 2, 3])

实际上,我们可以从一片a中切出一个序列,其中仅包含原始的2行:

In [18]: pd.Series(a[:2])
Out[18]: 
0    [0, 1, 2, 3]
1    [4, 5, 6, 7]
dtype: object

在其他SO问题中已深入讨论了构造1d对象dtype数组的技巧。

请注意,这样的Series的行为不像多列DataFrame。我见过尝试编写csv文件的尝试,其中像这样的元素被保存为带引号的字符串。


让我们比较一些施工时间:

制作2种类型的较大数组:

In [25]: a0 = np.ones([1000,4],int)
In [26]: a1 = np.empty(1000, object)
In [27]: a1[:] = [np.ones(4,int) for _ in range(1000)]
# a1[:] = list(a0)   # faster

首先创建一个DataFrame:

In [28]: timeit pd.DataFrame(a0)
136 µs ± 919 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

这与Out[3]的时间相同;显然,制作带有values的二维数组(任何大小)的DataFrame的开销。

像您一样制作系列:

In [29]: timeit pd.Series(list(a0))
434 µs ± 12.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [30]: timeit pd.Series(a0.tolist())
315 µs ± 5.64 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

这两者都比小型a长,反映了创作的迭代本质。

并使用一维对象数组:

In [31]: timeit pd.Series(a1)
103 µs ± 1.66 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

这与小型1d阵列相同。与In[28]一样,我认为创建Series对象,然后为其分配一个未更改的值数组只是开销。

现在构造a1数组比较慢。

a1之类的对象数组在许多方面都类似于列表,它包含指向内存中其他位置的对象的指针。如果元素的类型不同(例如,包括字符串或无),则可能很有用,但在计算上不等同于2d数组。


总而言之,如果源数组确实是1d对象dtype数组,则可以从中快速创建一个Series。如果它确实是2d数组,则需要以某种方式将其首先转换为列表或1d对象数组。

答案 1 :(得分:0)

您可以从公共长度列表或列表列表的字典中制作DataFrame。在前一种情况下,大熊猫将键转换为列名,将列表转换为列值,在后一种情况下,每个列表均视为行。

import numpy as np 
import pandas as pd

a = np.array([np.array([0,1,2,3]), np.array([4,5,6,7])])
df = pd.DataFrame()
df['a'] = a.tolist()
df

输出:

    a
0   [0, 1, 2, 3]
1   [4, 5, 6, 7]