将pandas的数据框转换为NumPy数组。

698

我如何将pandas数据框转换为NumPy数组?

数据框:

import numpy as np
import pandas as pd

index = [1, 2, 3, 4, 5, 6, 7]
a = [np.nan, np.nan, np.nan, 0.1, 0.1, 0.1, 0.1]
b = [0.2, np.nan, 0.2, 0.2, 0.2, np.nan, np.nan]
c = [np.nan, 0.5, 0.5, np.nan, 0.5, 0.5, np.nan]
df = pd.DataFrame({'A': a, 'B': b, 'C': c}, index=index)
df = df.rename_axis('ID')

提供

label   A    B    C
ID                                 
1   NaN  0.2  NaN
2   NaN  NaN  0.5
3   NaN  0.2  0.5
4   0.1  0.2  NaN
5   0.1  0.2  0.5
6   0.1  NaN  0.5
7   0.1  NaN  NaN

我想将这个转换为NumPy数组,如下:

array([[ nan,  0.2,  nan],
       [ nan,  nan,  0.5],
       [ nan,  0.2,  0.5],
       [ 0.1,  0.2,  nan],
       [ 0.1,  0.2,  0.5],
       [ 0.1,  nan,  0.5],
       [ 0.1,  nan,  nan]])

另外,是否可以保留数据类型,像这样?

array([[ 1, nan,  0.2,  nan],
       [ 2, nan,  nan,  0.5],
       [ 3, nan,  0.2,  0.5],
       [ 4, 0.1,  0.2,  nan],
       [ 5, 0.1,  0.2,  0.5],
       [ 6, 0.1,  nan,  0.5],
       [ 7, 0.1,  nan,  nan]],
     dtype=[('ID', '<i4'), ('A', '<f8'), ('B', '<f8'), ('B', '<f8')])

15
为什么你需要这个?数据框架不是基于NumPy数组的吗?你应该可以在需要NumPy数组的地方使用数据框架。这就是为什么你可以在scikit-learn中使用数据框架,而函数要求提供NumPy数组。 - chrisfs
以下是关于dtypes和recarrays(也称为记录数组或结构化数组)的一些可能相关的链接:(1) https://dev59.com/O2kw5IYBdhLWcg3wXZX8 (2) https://stackoverflow.com/questions/52579601/convert-dataframe-with-strings-to-a-record-array - JohnE
2
注意:像这样将Pandas DataFrame转换为数组(或列表)可能表明存在其他问题。我强烈建议确保DataFrame是您特定用例的适当数据结构,并且Pandas不包括执行您感兴趣的操作的任何方法。 - AMC
16个回答

606

使用 df.to_numpy()

它比df.values更好,原因如下。*

现在是时候停止使用valuesas_matrix()了。

pandas v0.24.0引入了两个新方法,用于从pandas对象中获取NumPy数组:

  1. to_numpy(),定义在IndexSeries和DataFrame对象上;
  2. array,只在IndexSeries对象上定义。

如果您访问.values的v0.24文档,您将看到一个大大的红色警告,上面写着:

警告:我们建议使用DataFrame.to_numpy()代替。

有关更多信息,请参见v0.24.0发布说明此答案

*- to_numpy()是我推荐的方法,适用于任何需要可靠运行多个版本的生产代码。但是,如果您只是在jupyter或终端中制作一个草稿,使用.values来节省几毫秒的打字时间是可以接受的例外情况。您随时可以稍后添加fit n finish。



向更好的一致性迈进:to_numpy()

为了更好地保持API的一致性,引入了一个新方法to_numpy,用于从数据框中提取底层NumPy数组。

# Setup
df = pd.DataFrame(data={'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}, 
                  index=['a', 'b', 'c'])

# Convert the entire DataFrame
df.to_numpy()
# array([[1, 4, 7],
#        [2, 5, 8],
#        [3, 6, 9]])

# Convert specific columns
df[['A', 'C']].to_numpy()
# array([[1, 7],
#        [2, 8],
#        [3, 9]])

如上所述,这种方法还在IndexSeries对象上进行了定义(参见此处)。

df.index.to_numpy()
# array(['a', 'b', 'c'], dtype=object)

df['A'].to_numpy()
#  array([1, 2, 3])

默认情况下,返回视图,因此任何修改都会影响原始内容。

v = df.to_numpy()
v[0, 0] = -1
 
df
   A  B  C
a -1  4  7
b  2  5  8
c  3  6  9

如果你需要一份副本,可以使用to_numpy(copy=True)


pandas >= 1.0 更新 ExtensionTypes

如果你正在使用 pandas 1.x,很有可能会更多地涉及扩展类型。你需要更加小心确保这些扩展类型正确地转换。

a = pd.array([1, 2, None], dtype="Int64")                                  
a                                                                          

<IntegerArray>
[1, 2, <NA>]
Length: 3, dtype: Int64 

# Wrong
a.to_numpy()                                                               
# array([1, 2, <NA>], dtype=object)  # yuck, objects

# Correct
a.to_numpy(dtype='float', na_value=np.nan)                                 
# array([ 1.,  2., nan])

# Also correct
a.to_numpy(dtype='int', na_value=-1)
# array([ 1,  2, -1])

这在文档中被称为“整数数组现在使用Pandas NA”


如果您需要结果中的dtypes...

如另一个答案所示,DataFrame.to_records是一个很好的方法。

df.to_records()
# rec.array([('a', 1, 4, 7), ('b', 2, 5, 8), ('c', 3, 6, 9)],
#           dtype=[('index', 'O'), ('A', '<i8'), ('B', '<i8'), ('C', '<i8')])

不幸的是,to_numpy 无法完成此任务。但是你可以使用 np.rec.fromrecords 作为替代方法:

v = df.reset_index()
np.rec.fromrecords(v, names=v.columns.tolist())
# rec.array([('a', 1, 4, 7), ('b', 2, 5, 8), ('c', 3, 6, 9)],
#           dtype=[('index', '<U1'), ('A', '<i8'), ('B', '<i8'), ('C', '<i8')])

就性能而言,它们几乎相同(实际上,使用rec.fromrecords会稍微快一些)。

df2 = pd.concat([df] * 10000)

%timeit df2.to_records()
%%timeit
v = df2.reset_index()
np.rec.fromrecords(v, names=v.columns.tolist())

12.9 ms ± 511 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.56 ms ± 291 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


新增方法的原因

在两个GitHub问题GH19954GH23623的讨论下,除了array之外,还添加了to_numpy()

具体来说,文档中提到了以下原因:

[...] 使用.values时,返回值是否为实际数组、其某种转换或Pandas定制数组(如Categorical)是不清楚的。例如,对于PeriodIndex,每次使用.values都会生成一个新的周期对象的ndarray。[...]

to_numpy旨在提高API的一致性,这是正确方向的重要一步。当前版本不会弃用.values,但我预计在未来某个时候可能会发生这种情况,所以我建议用户尽快迁移到较新的API。



对其他解决方案的批评

DataFrame.values的行为不一致,正如先前所述。

DataFrame.get_values()在v1.0中被静默删除,之前在v0.25中被弃用。在此之前,它只是DataFrame.values的一个包装器,因此上面所说的所有内容都适用。

DataFrame.as_matrix()在v1.0中被删除,在v0.23中被弃用。 请勿使用!


2
我不明白为什么要一页页地看着人们尖叫着要从as_matrix切换到另一个解决方案,比如to_numpy,却没有解释如何恢复as_matrix的列选择功能!我相信还有其他选择列的方法,但as_matrix至少是其中之一! - Jérémie
2
除了明显的 df[[col1, col2']].to_numpy(),@Jérémie 你还有什么想法吗?不确定为什么你认为想要宣传一个更新的替代已弃用函数的想法会导致回答被踩。 - cs95
如果某些列是列表类型,我该如何创建一个扁平的数组? - Moniba
@Moniba,根据您的需求,您可能需要将列表项分解为单独的列/行。 - cs95
这似乎只能转换值。如何将列和/或行名称以及值转换为numpy数组?我需要进行两次单独的调用,然后以某种方式合并结果吗?谢谢。 - Confounded
显示剩余11条评论

461

将pandas dataframe(df)转换为numpy ndarray,请使用此代码:

df.values

array([[nan, 0.2, nan],
       [nan, nan, 0.5],
       [nan, 0.2, 0.5],
       [0.1, 0.2, nan],
       [0.1, 0.2, 0.5],
       [0.1, nan, 0.5],
       [0.1, nan, nan]])

20
这不再是推荐的方法了! - Marine Galantin
也许可以提及这是针对低于v0.24版本的内容。 - joel

131

注意:在这个答案中使用的.as_matrix()方法已经被弃用。Pandas 0.23.4会发出警告:

.as_matrix方法将在未来版本中移除。请使用.values代替。


Pandas中内置有一些东西...

numpy_matrix = df.as_matrix()

提供

array([[nan, 0.2, nan],
       [nan, nan, 0.5],
       [nan, 0.2, 0.5],
       [0.1, 0.2, nan],
       [0.1, 0.2, 0.5],
       [0.1, nan, 0.5],
       [0.1, nan, nan]])

31
这不会生成结构化数组,所有列的数据类型都是“object”。 - sebix
15
自版本0.23.0起已经弃用:请改用DataFrame.values。此方法是为了向后兼容而提供的。通常建议使用“.values”。 - David J.
9
现在已经过时了。从v0.24开始,请改用 to_numpy(也不要用 .values)。更多信息请参见 这里 - cs95
1
"FutureWarning: 方法 .as_matrix 将在未来版本中被移除。请使用 .values 替代。" - Farhad Maleki

74

我只需将 DataFrame.reset_index()DataFrame.values 函数链接起来,就可以获得包括索引在内的数据框的Numpy表示:

In [8]: df
Out[8]: 
          A         B         C
0 -0.982726  0.150726  0.691625
1  0.617297 -0.471879  0.505547
2  0.417123 -1.356803 -1.013499
3 -0.166363 -0.957758  1.178659
4 -0.164103  0.074516 -0.674325
5 -0.340169 -0.293698  1.231791
6 -1.062825  0.556273  1.508058
7  0.959610  0.247539  0.091333

[8 rows x 3 columns]

In [9]: df.reset_index().values
Out[9]:
array([[ 0.        , -0.98272574,  0.150726  ,  0.69162512],
       [ 1.        ,  0.61729734, -0.47187926,  0.50554728],
       [ 2.        ,  0.4171228 , -1.35680324, -1.01349922],
       [ 3.        , -0.16636303, -0.95775849,  1.17865945],
       [ 4.        , -0.16410334,  0.0745164 , -0.67432474],
       [ 5.        , -0.34016865, -0.29369841,  1.23179064],
       [ 6.        , -1.06282542,  0.55627285,  1.50805754],
       [ 7.        ,  0.95961001,  0.24753911,  0.09133339]])

为了获取数据类型,我们需要使用view将这个ndarray转换成一个结构化数组:

In [10]: df.reset_index().values.ravel().view(dtype=[('index', int), ('A', float), ('B', float), ('C', float)])
Out[10]:
array([( 0, -0.98272574,  0.150726  ,  0.69162512),
       ( 1,  0.61729734, -0.47187926,  0.50554728),
       ( 2,  0.4171228 , -1.35680324, -1.01349922),
       ( 3, -0.16636303, -0.95775849,  1.17865945),
       ( 4, -0.16410334,  0.0745164 , -0.67432474),
       ( 5, -0.34016865, -0.29369841,  1.23179064),
       ( 6, -1.06282542,  0.55627285,  1.50805754),
       ( 7,  0.95961001,  0.24753911,  0.09133339),
       dtype=[('index', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

3
唯一缺少的是如何从数据框中构建 dtype 以便编写通用函数。 - Joseph Garvin

38
您可以使用to_records方法,但如果数据类型不符合您的要求,则必须进行一些操作。在我的情况下,从字符串中复制您的DF后,索引类型为字符串(在pandas中表示为object dtype)。
In [102]: df
Out[102]: 
label    A    B    C
ID                  
1      NaN  0.2  NaN
2      NaN  NaN  0.5
3      NaN  0.2  0.5
4      0.1  0.2  NaN
5      0.1  0.2  0.5
6      0.1  NaN  0.5
7      0.1  NaN  NaN

In [103]: df.index.dtype
Out[103]: dtype('object')
In [104]: df.to_records()
Out[104]: 
rec.array([(1, nan, 0.2, nan), (2, nan, nan, 0.5), (3, nan, 0.2, 0.5),
       (4, 0.1, 0.2, nan), (5, 0.1, 0.2, 0.5), (6, 0.1, nan, 0.5),
       (7, 0.1, nan, nan)], 
      dtype=[('index', '|O8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])
In [106]: df.to_records().dtype
Out[106]: dtype([('index', '|O8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

我无法将recarray的dtype转换,但在Pandas中可以这样做:

In [109]: df.index = df.index.astype('i8')
In [111]: df.to_records().view([('ID', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])
Out[111]:
rec.array([(1, nan, 0.2, nan), (2, nan, nan, 0.5), (3, nan, 0.2, 0.5),
       (4, 0.1, 0.2, nan), (5, 0.1, 0.2, 0.5), (6, 0.1, nan, 0.5),
       (7, 0.1, nan, nan)], 
      dtype=[('ID', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])
请注意,Pandas在导出的记录数组中没有正确设置索引的名称(为ID)(是一个错误吗?),因此我们从类型转换中获益,以便进行更正。 目前,Pandas仅具有8字节整数i8和浮点数f8(请参见此问题)。

2
要获得所需的结构化数组(其性能优于recarray),只需将recarray传递给np.array构造函数即可。 - meteore
我们刚刚修复了设置上面显示的索引名称的问题。 - Chang She

31

看起来 df.to_records() 适合你的需求。你正在寻找的确切功能已被请求,并且 to_records 被指定为替代方法。这个问题

我在本地使用你的示例尝试了一下,该调用产生了与您寻找的输出非常相似的结果:

rec.array([(1, nan, 0.2, nan), (2, nan, nan, 0.5), (3, nan, 0.2, 0.5),
       (4, 0.1, 0.2, nan), (5, 0.1, 0.2, 0.5), (6, 0.1, nan, 0.5),
       (7, 0.1, nan, nan)],
      dtype=[(u'ID', '<i8'), (u'A', '<f8'), (u'B', '<f8'), (u'C', '<f8')])

请注意,这是一个recarray而不是array。您可以通过调用其构造函数将结果移入常规的numpy数组中:np.array(df.to_records())


5
等等,与 @meteore 在五年前提到的 to_records() 相比,这个答案增加了什么内容? - JohnE

17

试试这个:

a = numpy.asarray(df)

1
你好!请在你的回答中添加一些解释。目前,由于长度内容的原因,它被审核标记为低质量,并有被系统删除的风险。谢谢! - d_kennetz
1
基本上将输入转换为数组(如名称所示)。因此,结合问题的背景,这个答案是有效的。请查看https://docs.scipy.org/doc/numpy/reference/generated/numpy.asarray.html#numpy-asarray。 - Lautaro Parada Opazo
2
谢谢,我认为这有点是不言自明的。 - Dadu Khan

10

这里是我从pandas DataFrame创建结构数组的方法。

创建数据帧

import pandas as pd
import numpy as np
import six

NaN = float('nan')
ID = [1, 2, 3, 4, 5, 6, 7]
A = [NaN, NaN, NaN, 0.1, 0.1, 0.1, 0.1]
B = [0.2, NaN, 0.2, 0.2, 0.2, NaN, NaN]
C = [NaN, 0.5, 0.5, NaN, 0.5, 0.5, NaN]
columns = {'A':A, 'B':B, 'C':C}
df = pd.DataFrame(columns, index=ID)
df.index.name = 'ID'
print(df)

      A    B    C
ID               
1   NaN  0.2  NaN
2   NaN  NaN  0.5
3   NaN  0.2  0.5
4   0.1  0.2  NaN
5   0.1  0.2  0.5
6   0.1  NaN  0.5
7   0.1  NaN  NaN

定义一个函数,从Pandas DataFrame创建一个NumPy结构数组(而不是记录数组)。

def df_to_sarray(df):
    """
    Convert a pandas DataFrame object to a numpy structured array.
    This is functionally equivalent to but more efficient than
    np.array(df.to_array())

    :param df: the data frame to convert
    :return: a numpy structured array representation of df
    """

    v = df.values
    cols = df.columns

    if six.PY2:  # python 2 needs .encode() but 3 does not
        types = [(cols[i].encode(), df[k].dtype.type) for (i, k) in enumerate(cols)]
    else:
        types = [(cols[i], df[k].dtype.type) for (i, k) in enumerate(cols)]
    dtype = np.dtype(types)
    z = np.zeros(v.shape[0], dtype)
    for (i, k) in enumerate(z.dtype.names):
        z[k] = v[:, i]
    return z

使用reset_index来创建一个包含索引作为数据一部分的新数据框。将该数据框转换为结构数组。

sa = df_to_sarray(df.reset_index())
sa

array([(1L, nan, 0.2, nan), (2L, nan, nan, 0.5), (3L, nan, 0.2, 0.5),
       (4L, 0.1, 0.2, nan), (5L, 0.1, 0.2, 0.5), (6L, 0.1, nan, 0.5),
       (7L, 0.1, nan, nan)], 
      dtype=[('ID', '<i8'), ('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

编辑:更新df_to_sarray以避免在Python 3中调用.encode()时出错。感谢Joseph Garvinhalcyon的评论和解决方案。


不适用于我,错误:TypeError:数据类型不被理解。 - Joseph Garvin
感谢您的评论,也感谢 halcyon 的纠正。我已经更新了我的答案,希望现在能够对您有所帮助。 - Phil

8

一个更简单的示例DataFrame:

df

         gbm       nnet        reg
0  12.097439  12.047437  12.100953
1  12.109811  12.070209  12.095288
2  11.720734  11.622139  11.740523
3  11.824557  11.926414  11.926527
4  11.800868  11.727730  11.729737
5  12.490984  12.502440  12.530894

使用:

np.array(df.to_records().view(type=np.matrix))

GET:

array([[(0, 12.097439  , 12.047437, 12.10095324),
        (1, 12.10981081, 12.070209, 12.09528824),
        (2, 11.72073428, 11.622139, 11.74052253),
        (3, 11.82455653, 11.926414, 11.92652727),
        (4, 11.80086775, 11.72773 , 11.72973699),
        (5, 12.49098389, 12.50244 , 12.53089367)]],
dtype=(numpy.record, [('index', '<i8'), ('gbm', '<f8'), ('nnet', '<f4'),
       ('reg', '<f8')]))

7

我仔细阅读了以上的回答。"as_matrix()"方法可以使用,但它现在已经过时了。对我来说,".to_numpy()"方法可行。

这将返回一个多维数组。如果您从Excel表格读取数据并需要访问任何索引中的数据,则我更喜欢使用此方法。希望这有所帮助 :)


“_and you need to access data from any index_”是什么意思?根据您的数据性质,Pandas DataFrame可能并不是首选。 - AMC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,