Convierta el marco de datos de pandas a la matriz NumPy

467

Estoy interesado en saber cómo convertir un marco de datos de pandas en una matriz NumPy.

marco de datos:

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

da

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

Me gustaría convertir esto a una matriz NumPy, así:

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

¿Cómo puedo hacer esto?


Como beneficio adicional, ¿es posible preservar los dtypes, como este?

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

¿o similar?

mister.nobody.nz
fuente
55
Por qué necesitas esto ? ¿No están los marcos de datos basados ​​en matrices numpy de todos modos? Debería poder utilizar un marco de datos donde necesita una matriz numpy. Es por eso que puede usar marcos de datos con scikit-learn donde las funciones solicitan matrices numpy.
chrisfs
Aquí hay un par de enlaces posiblemente relevantes sobre dtypes y recarrays (también conocidos como arreglos de registros o arreglos estructurados): (1) stackoverflow.com/questions/9949427/… (2) stackoverflow.com/questions/52579601/…
JohnE
NOTA: Tener que convertir Pandas DataFrame a una matriz (o lista) como esta puede ser indicativo de otros problemas. Recomiendo encarecidamente asegurarse de que un DataFrame es la estructura de datos adecuada para su caso de uso particular, y que Pandas no incluye ninguna forma de realizar las operaciones que le interesan.
AMC

Respuestas:

391

Para convertir un marco de datos de pandas (df) en un ndarray numpy, use este código:

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]])
Usuario456898
fuente
239

Desprecia tu uso de valuesy as_matrix()!

pandas v0.24.0 introdujo dos nuevos métodos para obtener matrices NumPy de objetos pandas:

  1. to_numpy(), que se define en Index, Series,y DataFrameobjetos, y
  2. array, Que se define en Indexy Seriesobjetos solamente.

Si visita los documentos de v0.24 para .values, verá una gran advertencia roja que dice:

Advertencia: Recomendamos usar en su DataFrame.to_numpy()lugar.

Consulte esta sección de las notas de la versión v0.24.0 y esta respuesta para obtener más información.


Hacia una mejor consistencia: to_numpy()

En el espíritu de una mejor consistencia en toda la API, to_numpyse ha introducido un nuevo método para extraer la matriz subyacente NumPy de DataFrames.

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

df.to_numpy()
array([[1, 4],
       [2, 5],
       [3, 6]])

Como se mencionó anteriormente, este método también se define en Indexy Seriesobjetos (ver aquí ).

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

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

De forma predeterminada, se devuelve una vista, por lo que cualquier modificación realizada afectará al original.

v = df.to_numpy()
v[0, 0] = -1

df
   A  B
a -1  4
b  2  5
c  3  6

Si necesita una copia, use to_numpy(copy=True).

pandas> = 1.0 actualización para ExtensionTypes

Si está usando pandas 1.x, es probable que esté lidiando con tipos de extensión mucho más. Deberá tener un poco más de cuidado para que estos tipos de extensión se conviertan correctamente.

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

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

Esto se llama en los documentos .

Si necesitas el dtypes...

Como se muestra en otra respuesta, DataFrame.to_recordses una buena manera de hacer esto.

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

Esto no se puede hacer to_numpy, desafortunadamente. Sin embargo, como alternativa, puede usar np.rec.fromrecords:

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

En cuanto al rendimiento, es casi lo mismo (en realidad, el uso rec.fromrecordses un poco más rápido).

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

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

11.1 ms ± 557 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.67 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Justificación para agregar un nuevo método

to_numpy()(además de array) se agregó como resultado de las discusiones bajo dos temas de GitHub GH19954 y GH23623 .

Específicamente, los documentos mencionan la justificación:

[...] .valuesno estaba claro si el valor devuelto sería la matriz real, alguna transformación de la misma o una de las matrices personalizadas de pandas (como Categorical). Por ejemplo, con PeriodIndex, .values genera un nuevo objeto ndarrayde período cada vez. [...]

to_numpypretenden mejorar la consistencia de la API, que es un paso importante en la dirección correcta. .valuesno quedará en desuso en la versión actual, pero espero que esto pueda suceder en algún momento en el futuro, por lo que instaría a los usuarios a migrar hacia la API más nueva, tan pronto como sea posible.


Crítica de otras soluciones

DataFrame.values tiene un comportamiento inconsistente, como ya se señaló.

DataFrame.get_values()es simplemente una envoltura DataFrame.values, por lo que se aplica todo lo dicho anteriormente.

DataFrame.as_matrix()ahora está en desuso, ¡ NO lo use!

cs95
fuente
¡No entiendo cómo es posible leer página tras página tras página de personas que gritan a toda velocidad para cambiar de as_matrixotra solución, en este caso, to_numpysin explicar cómo recuperar la funcionalidad de selección de columna de as_matrix! Estoy seguro de que hay otras formas de seleccionar columnas, ¡pero as_matrixal menos era una de ellas!
Jérémie
@ Jérémie además de lo obvio df[[col1, col2']].to_numpy()? No estoy seguro de por qué cree que desear anunciar una alternativa actualizada a una función obsoleta garantiza un voto negativo en la respuesta.
cs95
¿Qué pasa si algunas de las columnas son de tipo de lista? ¿Cómo puedo crear una matriz plana llena de baches a partir de esto?
Moniba
@Moniba es posible que desee desglosar los elementos de la lista en columnas / filas separadas según su requisito primero.
cs95
A menos que me equivoque, al obtener más de una columna en la misma llamada, todos los datos se fusionan en una gran matriz. ¿Me estoy perdiendo de algo?
Andrea Moro
128

Nota : El .as_matrix()método utilizado en esta respuesta está en desuso. Pandas 0.23.4 advierte:

El método .as_matrixse eliminará en una versión futura. Use .values ​​en su lugar.


Pandas tiene algo construido en ...

numpy_matrix = df.as_matrix()

da

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]])
ZJS
fuente
30
Esto no proporciona una matriz estructurada, todas las columnas son de tipo d object.
sebix
14
"En desuso desde la versión 0.23.0: utilice DataFrame.values ​​en su lugar". / "Este método se proporciona por compatibilidad con versiones anteriores. En general, se recomienda usar '.values'". - github.com/pandas-dev/pandas/blob/…
David J.
44
Esto ahora está en desuso. A partir de la v0.24 en adelante, úselo en su to_numpylugar ( .valuestampoco). Más aquí .
cs95
1
"FutureWarning: el método .as_matrix se eliminará en una versión futura. Utilice .values ​​en su lugar".
Farhad Maleki,
66

Simplemente encadenaría las funciones DataFrame.reset_index () y DataFrame.values para obtener la representación Numpy del marco de datos, incluido el índice:

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

Para obtener los dtypes, necesitaríamos transformar este ndarray en una matriz estructurada usando view :

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')])
prl900
fuente
3
Lo único que falta en esta respuesta es cómo construir el dtype a partir del marco de datos para que pueda escribir una función genérica
Joseph Garvin
32

Puedes usar el to_recordsmétodo, pero tienes que jugar un poco con los dtypes si no son lo que quieres desde el principio. En mi caso, después de haber copiado su DF de una cadena, el tipo de índice es una cadena (representada por un objectdtype en pandas):

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

Convertir el dtype de recarray no funciona para mí, pero ya se puede hacer esto en 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')])

Tenga en cuenta que Pandas no establece el nombre del índice correctamente (para ID ) en la matriz de registros exportados (¿un error?), Por lo que nos beneficiamos de la conversión de tipos para corregir también eso.

Por el momento, Pandas solo tiene enteros de 8 bytes i8, y flota, f8(vea este número ).

meteore
fuente
2
Para obtener la matriz estructurada deseada (que tiene un mejor rendimiento que un recarray), simplemente pase el recarray al np.arrayconstructor.
meteore
Solo ponemos una solución para configurar el nombre del índice que se muestra arriba.
Chang She
26

Parece df.to_records()que funcionará para ti. Se solicitó la función exacta que está buscando y se to_recordsseñaló como alternativa.

Probé esto localmente usando su ejemplo, y esa llamada produce algo muy similar al resultado que estaba buscando:

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

Tenga en cuenta que esto es recarraymás que un array. Puede mover el resultado a una matriz numpy normal llamando a su constructor como np.array(df.to_records()).

Jamie Doyle
fuente
3
Espera, ¿qué agrega esta respuesta en comparación con la otra respuesta de @meteore que mencionó hace to_records()más de 5 años?
JohnE
13

Prueba esto:

a = numpy.asarray(df)
Dadu Khan
fuente
¡Hola! Agregue alguna explicación a su respuesta. En este momento, se está marcando como de baja calidad por revisión debido a la longitud y el contenido y está en riesgo de ser eliminado por el sistema. ¡Gracias!
d_kennetz
1
básicamente convierte la entrada a una matriz (como su nombre lo indica). Entonces, junto con el contexto de la pregunta, esta respuesta es válida. consulte docs.scipy.org/doc/numpy/reference/generated/…
Lautaro Parada Opazo
Gracias, creo que se explica por sí mismo.
Dadu Khan
8

Aquí está mi enfoque para hacer una matriz de estructura a partir de un DataFrame de pandas.

Crea el marco de datos

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

Defina la función para hacer una matriz de estructura numpy (no una matriz de registros) a partir de un DataFrame de pandas.

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

Utilícelo reset_indexpara crear un nuevo marco de datos que incluya el índice como parte de sus datos. Convierta ese marco de datos en una matriz de estructura.

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

EDITAR: Se actualizó df_to_sarray para evitar errores al llamar a .encode () con python 3. Gracias a Joseph Garvin y halcyon por sus comentarios y soluciones.

Phil
fuente
no funciona para mí, error: Error de tipo: tipo de datos no entendido
Joseph Garvin
Gracias por tu comentario y por halcyon por la corrección. Actualicé mi respuesta, así que espero que funcione para ti ahora.
Phil
5

Una forma más simple de ejemplo de 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

UTILIZAR:

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

OBTENER:

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')]))
Yanni Papadakis
fuente
4

Simplemente tuve un problema similar al exportar desde el marco de datos a la tabla arcgis y tropecé con una solución de usgs ( https://my.usgs.gov/confluence/display/cdi/pandas.DataFrame+to+ArcGIS+Table ). En resumen, su problema tiene una solución similar:

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

np_data = np.array(np.rec.fromrecords(df.values))
np_names = df.dtypes.index.tolist()
np_data.dtype.names = tuple([name.encode('UTF8') for name in np_names])

np_data

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)], 
      dtype=(numpy.record, [('A', '<f8'), ('B', '<f8'), ('C', '<f8')]))
lars
fuente
4

Revisé las respuestas anteriores. El método " as_matrix () " funciona pero ahora está obsoleto. Para mí, lo que funcionó fue " .to_numpy () ".

Esto devuelve una matriz multidimensional. Preferiré usar este método si está leyendo datos de la hoja de Excel y necesita acceder a datos desde cualquier índice. Espero que esto ayude :)

Arsam
fuente
¿Qué quiere decir con y necesita acceder a los datos desde cualquier índice ? Dependiendo de la naturaleza de sus datos, un Pandas DataFrame puede no ser la opción correcta en primer lugar.
AMC
2

Además de la respuesta de meteore, encontré el código

df.index = df.index.astype('i8')

no funciona para mi Así que puse mi código aquí para la conveniencia de otros atascados con este problema.

city_cluster_df = pd.read_csv(text_filepath, encoding='utf-8')
# the field 'city_en' is a string, when converted to Numpy array, it will be an object
city_cluster_arr = city_cluster_df[['city_en','lat','lon','cluster','cluster_filtered']].to_records()
descr=city_cluster_arr.dtype.descr
# change the field 'city_en' to string type (the index for 'city_en' here is 1 because before the field is the row index of dataframe)
descr[1]=(descr[1][0], "S20")
newArr=city_cluster_arr.astype(np.dtype(descr))
James L
fuente
1

Una forma sencilla de convertir el marco de datos en una matriz numpy:

import pandas as pd
df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
df_to_array = df.to_numpy()
array([[1, 3],
   [2, 4]])

Se recomienda el uso de to_numpy para preservar la consistencia.

Referencia: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_numpy.html

usuario1460675
fuente
¿Cuál es la diferencia entre la solución proporcionada por Arsam y la suya ...
qaiser
Solo traté de hacerlo más completo y utilizable con un ejemplo de código, que es lo que personalmente prefiero.
user1460675
1

Prueba esto:

np.array(df) 

array([['ID', nan, nan, nan],
   ['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=object)

Más información en: [ https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html] Válido para numpy 1.16.5 y pandas 0.25.2.

Hermes Morales
fuente