Конвертируйте pandas dataframe в массив NumPy

468

Я заинтересован в том, чтобы узнать, как преобразовать пандасный массив данных в массив NumPy.

dataframe:

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

Как я могу это сделать?


В качестве бонуса, возможно ли сохранить dtypes, как это?

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

или похожие?

mister.nobody.nz
источник
5
Зачем вам это ? Разве информационные кадры не основаны на массивах в любом случае? Вы должны быть в состоянии использовать фрейм данных, где вам нужен массив NumPy. Вот почему вы можете использовать dataframes с scikit-learn, когда функции запрашивают массивы.
Крисфи
Вот несколько возможных ссылок на dtypes & recarrays (или массивы записей или структурированные массивы): (1) stackoverflow.com/questions/9949427/… (2) stackoverflow.com/questions/52579601/…
JohnE
ПРИМЕЧАНИЕ. Необходимость преобразования Pandas DataFrame в массив (или список), подобный этому, может свидетельствовать о других проблемах. Я настоятельно рекомендую убедиться, что DataFrame является подходящей структурой данных для вашего конкретного случая использования, и что Pandas не включает какой-либо способ выполнения операций, которые вас интересуют.
AMC

Ответы:

392

Чтобы преобразовать pandas dataframe (df) в пустой массив 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]])
User456898
источник
240

Устаревайте ваше использование 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

Если вы используете 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

# 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()устарел сейчас, не используйте!

cs95
источник
Я не понимаю, как можно читать страницу за страницей за страницей людей, кричащих наверху своих легких, чтобы переключиться as_matrixна другое решение, в этом случае, to_numpyне объясняя, как восстановить функциональность выбора столбца as_matrix! Уверен, есть и другие способы выбора столбцов, но as_matrixбыл хотя бы один из них!
Жереми
@ Джереми, кроме очевидного df[[col1, col2']].to_numpy()? Не уверен, почему вы думаете, что объявление об обновленной альтернативе устаревшей функции требует отрицательного ответа.
cs95
Что делать, если некоторые столбцы имеют тип списка. Как я могу создать плоский неровный массив из этого?
Мониба
@Moniba вы можете сначала разбить элементы списка на отдельные столбцы / строки в соответствии с вашими требованиями.
cs95
Если я не ошибаюсь, при получении более одного столбца за один вызов все данные объединяются в один большой массив. Я что-то пропустил?
Андреа Моро
129

Примечание . .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]])
ZJS
источник
30
Это не дает структурированного массива, все столбцы имеют тип dtype object.
Sebix
14
"Устаревшее с версии 0.23.0: вместо этого используйте DataFrame.values." / "Этот метод предназначен для обратной совместимости. Как правило, рекомендуется использовать« .values ​​»." - github.com/pandas-dev/pandas/blob/…
Дэвид Дж
4
Это сейчас не рекомендуется. Начиная с версии 0.24, пожалуйста, используйте to_numpyвместо (не .valuesлибо). Больше здесь .
cs95
1
"FutureWarning: метод .as_matrix будет удален в будущей версии. Вместо этого используйте .values."
Фархад Малеки
67

Я бы просто связал функции DataFrame.reset_index () и DataFrame.values, чтобы получить представление Numpy для dataframe, включая индекс:

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

Чтобы получить dtypes, нам нужно преобразовать этот 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')])
prl900
источник
3
единственное, чего не хватает в этом ответе, - как создать dtype из фрейма данных, чтобы вы могли написать обобщенную функцию
Джозеф Гарвин,
33

Вы можете использовать to_recordsметод, но вам придется немного поиграться с dtypes, если они не те, которые вы хотите с самого начала. В моем случае, скопировав ваш DF из строки, тип индекса - это строка (представленная в 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')])

Преобразование типа dar для recarray не работает для меня, но в 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(см. Эту проблему ).

meteore
источник
2
Чтобы получить искомый структурированный массив (который имеет лучшую производительность, чем повторный массив), вы просто передаете его в np.arrayконструктор.
Метеор
Мы просто добавили исправление для установки имени индекса, показанного выше.
Чанг Ше
27

Похоже, 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, вызвав его конструктор as np.array(df.to_records()).

Джейми Дойл
источник
3
Подождите, что этот ответ добавляет по сравнению с другим ответом @meteore, который упоминался to_records()более 5 лет назад?
Джон
14

Попробуй это:

a = numpy.asarray(df)
Даду Хан
источник
Привет! Пожалуйста, добавьте некоторые объяснения к вашему ответу. Прямо сейчас, это в настоящее время отмечено как низкое качество по обзору из - за длины и содержание , и существует риск того , чтобы быть удалена системой. Спасибо!
d_kennetz
1
в основном конвертировать входные данные в массив (как следует из названия). Таким образом, наряду с контекстом вопроса, этот ответ является действительным. проверьте docs.scipy.org/doc/numpy/reference/generated/…
Lautaro Parada Opazo
Спасибо, я думаю, это вроде само за себя.
Даду Хан
9

Вот мой подход к созданию массива структур из панд 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

Определите функцию для создания массива структур numy (не массив записей) из DataFrame 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

Используйте 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, чтобы избежать ошибки вызова .encode () с python 3. Спасибо Джозефу Гарвину и Halcyon за их комментарий и решение.

Фил
источник
не работает для меня, ошибка: Ошибка типа: тип данных не понят
Джозеф Гарвин
Спасибо за ваш комментарий и halcyon за исправление. Я обновил свой ответ, поэтому надеюсь, что он работает для вас сейчас.
Фил
6

Более простой способ для примера 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')]))
Янни Пападакис
источник
5

Просто была похожая проблема при экспорте из фрейма данных в таблицу arcgis и наткнулся на решение от 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')]))
Lars
источник
5

Я прошел ответы выше. Метод as_matrix () работает, но сейчас он устарел. Для меня то, что сработало, было " .to_numpy () ".

Это возвращает многомерный массив. Я предпочитаю использовать этот метод, если вы читаете данные из таблицы Excel и вам нужен доступ к данным из любого индекса. Надеюсь это поможет :)

Arsam
источник
Что вы имеете в виду, и вам нужно получить доступ к данным из любого индекса ? В зависимости от характера ваших данных, Pandas DataFrame может даже не быть правильным выбором.
AMC
3

В дополнение к ответу Метеора, я нашел код

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))
Джеймс Л
источник
2

Простой способ конвертировать dataframe в массив 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

user1460675
источник
В чем разница между решением, предоставленным Арсамом, и вашим ...
Qaiser
Просто попытался сделать его более полным и удобным с помощью примера кода, что я лично предпочитаю.
user1460675
2

Попробуй это:

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)

Некоторая дополнительная информация по адресу: [ https://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html] Действительно для numpy 1.16.5 и pandas 0.25.2.

Гермес Моралес
источник