Сегмент доступа к строке с правильным типом dtype

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