Перетворити фрейм даних панди в масив NumPy


465

Мені цікаво знати, як перетворити кадр даних панди в масив 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')])

чи подібне?


5
Навіщо вам це потрібно? Чи не є фрейми даних на основі масивних масивів? Ви повинні мати можливість використовувати фрейм даних там, де вам потрібен нумерований масив. Ось чому ви можете використовувати фрейми даних з scikit-learn, де функції запитують масивні масиви.
chrisfs

Ось кілька , можливо , відповідні посилання про dtypes і recarrays (ака записів масивів або структурованих масивів): (1) stackoverflow.com/questions/9949427 / ... (2) stackoverflow.com/questions/52579601 / ...
Johne

ПРИМІТКА. Перетворення конвертації Pandas DataFrame в масив (або список), подібний до цього, може свідчити про інші проблеми. Я настійно рекомендую переконатися, що DataFrame є відповідною структурою даних для вашого конкретного випадку використання, і що Pandas не містить жодного способу виконання операцій, які вас цікавлять.
AMC

Відповіді:


390

Для перетворення фрейму даних pandas (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]])

237

Припиніть використання valuesта as_matrix()!

pandas v0.24.0 представив два нові методи отримання масивів NumPy з об'єктів pandas:

  1. to_numpy(), що визначено на Index, Series,і DataFrameоб'єкти, і
  2. array, Який визначається на Indexі Seriesтільки об'єкти.

Якщо ви відвідаєте документи v0.24 для .values, ви побачите велике червоне попередження, яке говорить:

Попередження: радимо використовувати DataFrame.to_numpy()замість цього.

Дивіться цей розділ приміток до випуску v0.24.0 і цю відповідь для отримання додаткової інформації.


Назустріч кращій послідовності: to_numpy()

У дусі кращої послідовності в API to_numpyбуло введено новий метод для вилучення базового масиву NumPy з 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]])

Як було сказано вище, цей метод також визначений на об'єктах Indexі Series(див. Тут ).

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
a -1  4
b  2  5
c  3  6

Якщо вам потрібна копія, використовуйте to_numpy(copy=True).

pandas> = 1,0 оновлення для ExtensionTypes

Якщо ви використовуєте панди 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

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

Це називається в документах .

Якщо вам потрібно dtypes...

Як показано в іншій відповіді, DataFrame.to_recordsце хороший спосіб зробити це.

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

На to_numpyжаль, це неможливо зробити . Однак в якості альтернативи можна використовувати 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')])

Ефективність, майже однакова (фактично, використання rec.fromrecordsтрохи швидше).

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)

Обґрунтування додавання нового методу

to_numpy()(на додаток до array) було додано в результаті обговорень під двома випусками GitHub GH19954 та GH23623 .

Конкретно в документах згадується обгрунтування:

[...] з .valuesним було незрозуміло, чи буде повернене значення фактичним масивом, деяким перетворенням його чи одним із користувацьких масивів панди (як Categorical). Наприклад, з PeriodIndex, кожного разу .values створює нові ndarrayоб'єкти періоду. [...]

to_numpy прагнуть покращити узгодженість API, що є головним кроком у правильному напрямку. .valuesу поточній версії не буде застарілим, але я думаю, що це може статися в якийсь момент у майбутньому, тому я закликаю користувачів перейти до новішого API, як тільки ви зможете.


Критика інших рішень

DataFrame.values має непослідовну поведінку, як уже зазначалося.

DataFrame.get_values() це просто обгортка навколо DataFrame.values , тому все сказане вище стосується.

DataFrame.as_matrix()тепер застаріло, НЕ використовуйте!


Я не розумію, як можна читати сторінку за сторінкою за людьми, що кричать у верхній частині легень, щоб перейти as_matrixдо іншого рішення, в цьому випадку, to_numpyне пояснюючи, як відновити функцію вибору стовпця as_matrix! Я впевнений, що існують інші способи вибору стовпців, але as_matrixбув принаймні один із них!
Jérémie

@ Jérémie крім очевидного df[[col1, col2']].to_numpy()? Не впевнений, чому ви вважаєте, що бажання рекламувати оновлену альтернативу застарілої функції гарантує відповідь на відповідь.
cs95

що якщо деякі стовпці мають тип списку. Як я можу створити з цього плоский кулястий масив?
Моніба

@Moniba ви можете спочатку викласти елементи списку в окремі стовпці / рядки відповідно до вашої вимоги.
cs95

Якщо я не помиляюся, отримуючи більше одного стовпця в одному дзвінку, всі дані об’єднуються в один великий масив. Я щось пропускаю?
Андреа Моро

128

Примітка . .as_matrix()Метод, використаний у цій відповіді, застарів. Панди 0,23,4 попереджають:

Метод .as_matrixбуде видалено в майбутній версії. Використовуйте замість .values.


У пандах щось вбудоване ...

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

30
Це не дає структурованого масиву, усі стовпці мають dtype object.
sebix

14
"Застаріло з версії 0.23.0: замість цього використовуйте DataFrame.values." / "Цей метод передбачений для зворотної сумісності. Зазвичай рекомендується використовувати" .values ​​"." - github.com/pandas-dev/pandas/blob/…
Девід Дж

4
Зараз це застаріло. З версії v0.24, будь ласка, використовуйте to_numpyзамість цього (не .valuesбудь-який). Більше тут .
cs95

1
"FutureWarning: Метод .as_matrix буде видалений у наступній версії. Замість цього використовуйте .values."
Фархад

66

Я б просто ланцюг функцій 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]])

Щоб отримати типи, нам знадобиться перетворити цей ndarray в структурований масив, використовуючи 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')])

3
Єдине, чого не вистачає у цій відповіді, - як побудувати тип з кадру даних, щоб ви могли записати загальну функцію
Джозеф Гарвін

32

Ви можете використовувати to_recordsметод, але доведеться трохи пограти з типами, якщо вони не є тим, чого ви хочете отримати від руху. У моєму випадку, скопіювавши свій DF з рядка, тип індексу є рядковим (представлений objectтипом у пандах):

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

Перетворення типу зворотного виклику для мене не працює, але це можна зробити вже в 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
Щоб отримати затребуваний структурований масив (який має кращу ефективність, ніж повторний масив), ви просто передаєте np.arrayконструктор заново .
метеорит

Ми просто вкладаємо виправлення для встановлення назви індексу, показаного вище.
Чан Вона

26

Здається, 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()).


3
Зачекайте, що додає ця відповідь порівняно з іншою відповіддю @meteore, про яку згадувалося to_records()5 років раніше?
JohnE

13

Спробуйте це:

a = numpy.asarray(df)

Привіт! Будь ласка, додайте пояснення до своєї відповіді. Наразі він відзначається як низька якість при огляді через тривалість та вміст, і ризик буде видалений системою. Дякую!
d_kennetz

1
в основному перетворити вхід в масив (як підказує назва). Тож поряд з контекстом питання ця відповідь справедлива. перевірити docs.scipy.org/doc/numpy/reference/generated/…
Lautaro Parada Opazo

Дякую, я думаю, що це якесь самозрозуміле.
Даду Хан

8

Ось мій підхід до створення масиву структури з пандес 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

Визначте функцію створення масиву структури numpy (а не масив записів) з панд DataFrame.

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

EDIT: Оновлено df_to_sarray, щоб уникнути помилок при виклику .encode () з python 3. Дякую Джозефу Гарвіну та халсіону за коментар та рішення.


не працює для мене, помилка: TypeError: тип даних не зрозумів
Джозеф Гарвін

Дякуємо за ваш коментар та halcyon за виправлення. Я оновив свою відповідь, тож сподіваюся, що вона зараз працює для вас.
Філ


5

Простіший спосіб для прикладу 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))

Отримати:

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

4

Просто було подібну проблему під час експорту з фрейму даних до таблиці арггісу та натрапили на рішення з usgs ( https://my.usgs.gov/confluence/display/cdi/pandas.DataFrame+to+ArcGIS+Table ). Коротше кажучи, ваша проблема має подібне рішення:

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

4

Я переглянув відповіді вище. Метод " as_matrix () " працює, але його застаріло зараз. Для мене те, що працювало, було " .to_numpy () ".

Це повертає багатовимірний масив. Я вважаю за краще використовувати цей метод, якщо ви читаєте дані з листа excel і вам потрібно отримати доступ до даних з будь-якого індексу. Сподіваюся, це допомагає :)


Що ви маєте на увазі під і вам потрібно отримати доступ до даних з будь-якого індексу ? Залежно від характеру ваших даних, Pandas DataFrame може навіть не бути правильним вибором.
AMC

2

Після відповіді метеора я знайшов код

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

не працює для мене. Тому я ставлю свій код тут для зручності інших, що застрягли в цьому питанні.

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

1

Простий спосіб перетворення фрейму даних у масив 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]])

Використовувати to_numpy рекомендується для збереження консистенції.

Довідка: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_numpy.html


яка різниця між рішенням, наданим Арсамом, і вашим ...
qaiser

Просто спробував зробити його більш повним та зручним для використання на прикладі коду, який саме я віддаю перевагу.
користувач1460675

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.