#python #pandas #numpy #dtype
Вопрос:
У меня есть фрейм данных с несколькими различными типами в нем. Например:
df = pd.DataFrame({'A': ['A', 'B', 'C', 'D'],
'B': np.random.randint(10, size=4),
'C': np.random.randint(10, size=4),
'D': np.random.rand(4),
'E': np.random.rand(4)})
Типы dtypes являются
>>> df.dtypes
A object
B int32
C int32
D float64
E float64
dtype: object
Я хочу иметь возможность извлекать значения B
и C
в массиве numpy dtype np.int32
непосредственно из третьей строки df
. Кажется простым:
>>> df.iloc[2][['B', 'C']].to_numpy()
array([9, 9], dtype=object)
Это согласуется с тем фактом, что серия имеет тип object
:
>>> df.iloc[2]
A C
B 9
C 9
D 0.211487
E 0.857848
Name: 2, dtype: object
Так что, может быть, мне не стоит начинать ссору первым:
>>> df.loc[df.index[2], ['B', 'C']].to_numpy()
array([9, 9], dtype=object)
Все равно не везет. Конечно, я всегда могу постобработать и сделать
df.loc[df.index[2], ['B', 'C']].to_numpy().astype(np.int32)
Однако есть ли способ извлечь набор столбцов одного и того же типа dtype с их собственным типом dtype в массив numpy, используя только индексирование?
Комментарии:
1. Я никогда не думал об этой проблеме, потому что я так привык к цепочке индексов (ваш ответ V1). V2 был для меня в новинку.
2. @MichaelSzczesny. Новая версия Spyder на моей машине имеет несколько приятных функций автозаполнения. Не могу сказать, что я действительно проводил исследования для этого. Проблема в том, что сначала я двигался в неправильном направлении.
3. Я предполагаю, что это связано с тем фактом, как numpy хранит типы данных в массиве aan, если мы используем
iloc
и получаем фрейм данных с использованием индекса вместо ряда, мы видим предполагаемое поведение, поскольку при этом используются панды:df.iloc[[2],df.columns.get_indexer(['B','C'])].dtypes
4. @анки. По сути, серия представляет собой единый массив, поэтому должна поддерживать общий тип данных. Скорее всего, вам не нужен индексатор, последовательность
[2]
-это то, что делает его df.5. @анки. Я неправильно понял. Первые пару раз я не понимал, что такое индексатор. Моя ошибка.
Ответ №1:
V1
Ответ, конечно, шел в противоположном направлении от iloc
: сначала извлекались столбцы с согласованным типом dtype, чтобы строка могла быть непрерывным блоком:
>>> df[['B', 'C']].iloc[2]
array([9, 9])
Это говорит мне о том, что я не должен использовать панд напрямую, кроме как для загрузки своих данных для начала.
V2
Оказывается, что pd.DataFrame.to_numpy
и pd.Series.to_numpy
у dtype
вас есть аргумент, который вы можете использовать для преобразования. Это означает, что loc
iloc
подходы / тоже могут работать, хотя для этого все еще требуется дополнительное преобразование и априорное знание типа dtype:
>>> df.loc[d.index[2], ['B', 'C']].to_numpy(dtype=np.int32)
array([9, 9])
и
>>> df.iloc[2][['B', 'C']].to_numpy(dtype=np.int32)
array([9, 9])
Комментарии:
1. Я добавил несколько
._mgr
дисплеев, чтобы еще больше проиллюстрировать ваш ответ.2. @hpaulj. Это довольно интересно. У меня вроде как была мысленная модель, что именно так это делали панды, но никогда не было никаких новых деталей. Мне нравится решать проблемы, о существовании которых я и не подозревал
Ответ №2:
В качестве дополнения к ответу Сумасшедшего
In [107]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 A 4 non-null object
1 B 4 non-null int64
2 C 4 non-null int64
3 D 4 non-null float64
4 E 4 non-null float64
dtypes: float64(2), int64(2), object(1)
memory usage: 288.0 bytes
Я наткнулся на _mgr
то, что, по-видимому, управляет тем, как на самом деле хранятся данные. Похоже, он пытается сгруппировать столбцы типа dtype вместе, сохраняя данные в виде массивов (#col, #строка) :
In [108]: df._mgr
Out[108]:
BlockManager
Items: Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
Axis 1: RangeIndex(start=0, stop=4, step=1)
FloatBlock: slice(3, 5, 1), 2 x 4, dtype: float64
IntBlock: slice(1, 3, 1), 2 x 4, dtype: int64
ObjectBlock: slice(0, 1, 1), 1 x 4, dtype: object
Выбор 2 столбцов int:
In [109]: df[['B','C']]._mgr
Out[109]:
BlockManager
Items: Index(['B', 'C'], dtype='object')
Axis 1: RangeIndex(start=0, stop=4, step=1)
IntBlock: slice(0, 2, 1), 2 x 4, dtype: int64
и, следовательно, мы int
вводим массив dtype без дополнительных аргументов:
In [110]: df[['B','C']].values
Out[110]:
array([[5, 0],
[5, 0],
[0, 5],
[9, 9]])
Для случаев с одним блоком (например, для всех int
столбцов) values
данные фрейма являются (или, по крайней мере, могут быть) a view
. Но здесь, похоже, это не так.
Для одной строки:
In [116]: df.iloc[2]._mgr
Out[116]:
SingleBlockManager
Items: Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
ObjectBlock: 5 dtype: object
Выбор строки-это a Series
, поэтому не может быть смешанных типов dtypes фрейма данных.
Но «многорядный» выбор-это кадр
In [128]: df.iloc[2][['B','C']].values
Out[128]: array([0, 5], dtype=object)
In [129]: df.iloc[[2]][['B','C']].values
Out[129]: array([[0, 5]])