Converter panda dataframe em matriz NumPy

465

Estou interessado em saber como converter um dataframe de pandas em uma matriz NumPy.

quadro de dados:

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

Gostaria de converter isso em uma matriz NumPy, da seguinte maneira:

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

Como posso fazer isso?


Como bônus, é possível preservar os tipos, assim?

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

ou similar?

mister.nobody.nz
fonte
5
Por que você precisa disso? Os quadros de dados não são baseados em matrizes numpy de qualquer maneira? Você deve poder usar um quadro de dados em que precisa de uma matriz numpy. É por isso que você pode usar quadros de dados com o scikit-learn, onde as funções solicitam matrizes numpy.
Chrisfs
Aqui estão alguns links possivelmente relevantes sobre tipos e recarrays (também conhecidos como matrizes de registro ou matrizes estruturadas): (1) stackoverflow.com/questions/9949427/… (2) stackoverflow.com/questions/52579601/…
JohnE
NOTA: Ter que converter o Pandas DataFrame em uma matriz (ou lista) como essa pode ser um indicativo de outros problemas. Eu recomendo fortemente garantir que um DataFrame seja a estrutura de dados apropriada para o seu caso de uso específico e que o Pandas não inclua nenhuma maneira de executar as operações nas quais você está interessado.
AMC

Respostas:

391

Para converter um quadro de dados do pandas (df) em um 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]])
Usuário456898
fonte
239

Preterir o uso de valuese as_matrix()!

O pandas v0.24.0 introduziu dois novos métodos para obter matrizes NumPy a partir de objetos pandas:

  1. to_numpy(), que é definido em Index, Series,e DataFrameobjetos, e
  2. array, que é definido apenas Indexe Seriesobjetos.

Se você visitar os documentos da v0.24 para .values, verá um grande aviso vermelho dizendo:

Aviso: Recomendamos o uso DataFrame.to_numpy().

Consulte esta seção das notas da versão v0.24.0 e esta resposta para obter mais informações.


Rumo a uma melhor consistência: to_numpy()

No espírito de melhor consistência em toda a API, um novo método to_numpyfoi introduzido para extrair a matriz NumPy subjacente dos 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 mencionado acima, este método também é definido em Indexe Seriesobjetos (veja aqui ).

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

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

Por padrão, uma exibição é retornada, portanto, quaisquer modificações feitas afetarão o original.

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

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

Se você precisar de uma cópia, use to_numpy(copy=True).

pandas> = 1.0 atualização para ExtensionTypes

Se você estiver usando o pandas 1.x, é provável que esteja lidando com tipos de extensão muito mais. Você precisará ter um pouco mais de cuidado para que esses tipos de extensão sejam convertidos corretamente.

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

Isso é mencionado nos documentos .

Se você precisar do dtypes...

Como mostrado em outra resposta, DataFrame.to_recordsé uma boa maneira de fazer isso.

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

to_numpyInfelizmente, isso não pode ser feito . No entanto, como alternativa, você pode 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')])

Em termos de desempenho, é quase o mesmo (na verdade, usar rec.fromrecords é um pouco mais 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)

Justificativa para adicionar um novo método

to_numpy()(além de array) foi adicionado como resultado de discussões em duas edições do GitHub GH19954 e GH23623 .

Especificamente, os documentos mencionam a lógica:

[...] com .valuesisso não ficou claro se o valor retornado seria a matriz real, alguma transformação dela ou uma das matrizes personalizadas dos pandas (como Categorical). Por exemplo, com PeriodIndex, .values gera um novondarray objeto de ponto a cada vez. [...]

to_numpy visam melhorar a consistência da API, que é um passo importante na direção certa. .valuesnão será descontinuado na versão atual, mas espero que isso aconteça em algum momento no futuro. Por isso, exortaria os usuários a migrarem para a API mais recente assim que possível.


Crítica de outras soluções

DataFrame.values tem comportamento inconsistente, como já observado.

DataFrame.get_values() é simplesmente um invólucro DataFrame.values , então tudo o que foi dito acima se aplica.

DataFrame.as_matrix()está obsoleto agora, NÃO use!

cs95
fonte
Eu não entendo como é possível ler página após página após página de pessoas gritando no topo de seus pulmões para mudar as_matrixpara outra solução, neste caso, to_numpysem explicar como recuperar a funcionalidade de seleção da coluna as_matrix! Tenho certeza de que existem outras maneiras de selecionar colunas, mas as_matrixera pelo menos uma delas!
Jérémie
@ Jeremy além do óbvio df[[col1, col2']].to_numpy()? Não sabe por que você acha que querer anunciar uma alternativa atualizada a uma função obsoleta justifica um voto negativo na resposta.
#
what Se algumas das colunas forem do tipo lista. Como posso criar uma matriz acidentada plana com isso?
Moniba
@Moniba, convém explodir os itens da lista em colunas / linhas separadas, conforme sua necessidade primeiro.
precisa saber é
A menos que eu esteja errado, obter mais de uma coluna na mesma chamada faz com que todos os dados sejam mesclados em uma grande matriz. Estou esquecendo de algo?
Andrea Moro
128

Nota : O .as_matrix()método usado nesta resposta está obsoleto. O Pandas 0.23.4 adverte:

O método .as_matrixserá removido em uma versão futura. Use valores.


Pandas tem algo construído em ...

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]])
ZJS
fonte
30
Isso não fornece uma matriz estruturada, todas as colunas são do tipo object.
sebix
14
"Descontinuado desde a versão 0.23.0: use DataFrame.values." / "Este método é fornecido para compatibilidade com versões anteriores. Geralmente, é recomendável usar '.values'." - github.com/pandas-dev/pandas/blob/…
David J.
4
Agora está obsoleto. A partir da v0.24, use em to_numpyvez disso ( .valuestambém não ). Mais aqui .
Cs95
1
"FutureWarning: O método .as_matrix será removido em uma versão futura. Use .values."
Farhad Maleki
66

Gostaria apenas de encadear as funções DataFrame.reset_index () e DataFrame.values para obter a representação Numpy do quadro de dados, incluindo o í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 obter os tipos, precisamos transformar esse ndarray em uma matriz estruturada 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
fonte
3
a única coisa que falta nesta resposta é como construir o dtipo do quadro de dados de modo que você pode escrever uma função genérica
Joseph Garvin
32

Você pode usar o to_recordsmétodo, mas precisa brincar um pouco com os tipos, se eles não forem o que você deseja desde o início. No meu caso, depois de copiar seu DF de uma string, o tipo de índice é string (representado por um objectdtype no 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')])

Converter o dtype de recarray não funciona para mim, mas já é possível fazer isso no 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')])

Observe que o Pandas não define o nome do índice corretamente (para ID) na matriz de registros exportados (um bug?), Portanto, aproveitamos a conversão de tipos para também corrigir isso.

No momento, o Pandas possui apenas números inteiros de 8 bytes i8, e floats f8(consulte esta edição ).

meteore
fonte
2
Para obter a matriz estruturada procurada (que tem melhor desempenho do que um recarray), basta passar o recarray ao np.arrayconstrutor.
meteore 2/11/12
Nós apenas colocamos uma correção para definir o nome do índice mostrado acima.
Chang Ela
26

Parece que df.to_records()vai funcionar para você. O recurso exato que você estava procurando foi solicitado e to_recordsapontado como alternativa.

Eu tentei isso localmente usando o seu exemplo, e essa chamada gera algo muito semelhante à saída que você estava procurando:

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

Observe que este é um recarraye não um array. Você pode mover o resultado para uma matriz numpy regular chamando seu construtor como np.array(df.to_records()).

Jamie Doyle
fonte
3
Espere, o que essa resposta adiciona em comparação com a outra resposta de @meteore mencionada to_records()mais de 5 anos antes?
Johne
13

Tente o seguinte:

a = numpy.asarray(df)
Dadu Khan
fonte
Oi! Por favor, adicione algumas explicações à sua resposta. No momento, ele está sendo marcado como de baixa qualidade pela revisão devido ao tamanho e ao conteúdo e corre o risco de ser excluído pelo sistema. Obrigado!
d_kennetz 28/05
1
basicamente converta a entrada em uma matriz (como o nome sugere). Portanto, juntamente com o contexto da pergunta, esta resposta é válida. verifique docs.scipy.org/doc/numpy/reference/generated/…
Lautaro Parada Opazo
Obrigado, acho que é meio auto-explicativo.
Dadu Khan
8

Aqui está minha abordagem para criar uma matriz de estrutura a partir de um DataFrame do pandas.

Crie o quadro de dados

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 a função para criar uma matriz de estrutura numpy (não uma matriz de registros) de um DataFrame do 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

Use reset_indexpara criar um novo quadro de dados que inclua o índice como parte de seus dados. Converta esse quadro de dados em uma matriz de estrutura.

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

EDIT: Atualizado df_to_sarray para evitar erros ao chamar .encode () com python 3. Agradecemos a Joseph Garvin e halcyon por seus comentários e soluções.

Phil
fonte
não funciona para mim, erro: TypeError: tipo de dados não compreendido
Joseph Garvin
Obrigado pelo seu comentário e ao halcyon pela correção. Atualizei minha resposta, então espero que funcione para você agora.
Phil
5

Uma maneira mais simples para o exemplo 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

USAR:

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

PEGUE:

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
fonte
4

Só tive um problema semelhante ao exportar do dataframe para a tabela arcgis e deparei com uma solução da usgs ( https://my.usgs.gov/confluence/display/cdi/pandas.DataFrame+to+ArcGIS+Table ). Em suma, seu problema tem uma solução semelhante:

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
fonte
4

Eu passei pelas respostas acima. O método " as_matrix () " funciona, mas está obsoleto agora. Para mim, o que funcionou foi " .to_numpy () ".

Isso retorna uma matriz multidimensional. Prefiro usar esse método se você estiver lendo dados da planilha do Excel e precisar acessar dados de qualquer índice. Espero que isto ajude :)

Arsam
fonte
O que você quer dizer com e precisa acessar dados de qualquer índice ? Dependendo da natureza dos seus dados, um DataFrame do Pandas pode até não ser a escolha certa em primeiro lugar.
AMC
2

Além da resposta do meteore, encontrei o código

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

não funciona para mim. Então, coloquei meu código aqui para conveniência de outras pessoas envolvidas com esse 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
fonte
1

Uma maneira simples de converter o quadro de dados em 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]])

O uso de to_numpy é recomendado para preservar a consistência.

Referência: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_numpy.html

user1460675
fonte
qual é a diferença entre a solução fornecida pela Arsam e seu ...
qaiser
Apenas tentei torná-lo mais completo e utilizável com um exemplo de código, que é o que eu pessoalmente prefiro.
user1460675
1

Tente o seguinte:

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)

Mais informações em: [ https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html] Válido para numpy 1.16.5 e pandas 0.25.2.

Hermes Morales
fonte