Создание ссылки на набор данных HDF в H5py с использованием astype

Из документов h5py я вижу, что могу транслировать набор данных HDF в качестве другого типа с использованием метода astype для наборов данных. Это возвращает диспетчер контекста, который выполняет преобразование на лету.

Однако я хотел бы прочитать набор данных, хранящийся как uint16, а затем преобразовать его в тип float32. После этого я хотел бы извлечь различные срезы из этого набора данных в другой функции в качестве типа приведения float32. Документы объясняют использование как

with dataset.astype('float32'):
   castdata = dataset[:]

Это приведет к тому, что весь набор данных будет прочитан и преобразован в float32, чего я не хочу. Я хотел бы иметь ссылку на набор данных, но float32 эквивалентен numpy.astype. Как мне создать ссылку на объект .astype('float32'), чтобы я мог передать его другой функции для использования?

Пример:

import h5py as HDF
import numpy as np
intdata = (100*np.random.random(10)).astype('uint16')

# create the HDF dataset
def get_dataset_as_float():
    hf = HDF.File('data.h5', 'w')
    d = hf.create_dataset('data', data=intdata)
    print(d.dtype)
    # uint16

    with d.astype('float32'):
    # This won't work since the context expires. Returns a uint16 dataset reference
       return d

    # this works but causes the entire dataset to be read & converted
    # with d.astype('float32'):
    #   return d[:]

Кроме того, похоже, что контекст astype применяется только при доступе к элементам данных. Это означает, что

def use_data():
   d = get_data_as_float()
   # this is a uint16 dataset

   # try to use it as a float32
   with d.astype('float32'):
       print(np.max(d))   # --> output is uint16
       print(np.max(d[:]))   # --> output is float32, but entire data is loaded

Итак, нет ли способа использования astype в стиле numpy?


person achennu    schedule 11.08.2014    source источник
comment
Я не думаю, что np.max(d) делает здесь что-то особенно умное. Поскольку d не имеет собственного метода .max(), np.max() прочитает массив в память и вызовет для него np.core.umath.maximum.reduce(), используя d.dtype для установки типа вывода. Тайминги для np.max(d) и np.max(d[:]) почти идентичны.   -  person ali_m    schedule 11.08.2014
comment
@ali_m Возможно, ты прав. Я просто выбрал np.max как способ посмотреть, возвращает ли операция над массивом dtype. Для моих расчетов это не важно. В основном я извлекаю фрагменты, с которыми работаю.   -  person achennu    schedule 13.08.2014


Ответы (2)


d.astype() возвращает объект AstypeContext. Если вы посмотрите на исходный код AstypeContext, вы лучше поймете, что происходит:

class AstypeContext(object):

    def __init__(self, dset, dtype):
        self._dset = dset
        self._dtype = numpy.dtype(dtype)

    def __enter__(self):
        self._dset._local.astype = self._dtype

    def __exit__(self, *args):
        self._dset._local.astype = None

Когда вы вводите AstypeContext, атрибут ._local.astype вашего набора данных обновляется до нового желаемого типа, а когда вы выходите из контекста, он возвращается к исходному значению.

Таким образом, вы можете получить более или менее желаемое поведение следующим образом:

def get_dataset_as_type(d, dtype='float32'):

    # creates a new Dataset instance that points to the same HDF5 identifier
    d_new = HDF.Dataset(d.id)

    # set the ._local.astype attribute to the desired output type
    d_new._local.astype = np.dtype(dtype)

    return d_new

Теперь, когда вы читаете из d_new, вы получите float32 пустых массивов, а не uint16:

d = hf.create_dataset('data', data=intdata)
d_new = get_dataset_as_type(d, dtype='float32')

print(d[:])
# array([81, 65, 33, 22, 67, 57, 94, 63, 89, 68], dtype=uint16)
print(d_new[:])
# array([ 81.,  65.,  33.,  22.,  67.,  57.,  94.,  63.,  89.,  68.], dtype=float32)

print(d.dtype, d_new.dtype)
# uint16, uint16

Обратите внимание, что это не обновляет атрибут .dtype для d_new (который кажется неизменным). Если вы также хотите изменить атрибут dtype, вам, вероятно, потребуется создать подкласс h5py.Dataset, чтобы сделать это.

person ali_m    schedule 11.08.2014
comment
Интересно. Я изучил AsTypeContext, но не был уверен, что установка dtype самостоятельно приведет к нежелательным последствиям. Проведу некоторое тестирование и вернусь к этому ответу. Спасибо. - person achennu; 13.08.2014
comment
@arjmage Я думаю, все должно быть в порядке. d._local – это объект threading.local, поэтому ваши изменения должны потокобезопасный. Вы можете видеть здесь, что d._local.dtype просто используется для установки dtype выходного массива numpy, в который считываются данные. d.dtype фактически указывает на d.id.dtype, что идентификатор фактического объекта HDF5. - person ali_m; 13.08.2014

Документы astype, похоже, подразумевают, что чтение всего этого в новое место является его целью. Таким образом, ваш return d[:] является наиболее разумным, если вы хотите повторно использовать плавающее литье со многими функциями в отдельных случаях.

Если вы знаете, для чего вам нужен кастинг, и нужен он только один раз, вы можете поменять местами и сделать что-то вроде:

def get_dataset_as_float(intdata, *funcs):
    with HDF.File('data.h5', 'w') as hf:
        d = hf.create_dataset('data', data=intdata)
        with d.astype('float32'):
            d2 = d[...]
            return tuple(f(d2) for f in funcs)

В любом случае, вы хотите убедиться, что hf закрыто, прежде чем покинуть функцию, иначе вы столкнетесь с проблемами позже.

В общем, я бы предложил полностью разделить кастинг и загрузку/создание набора данных и передать набор данных в качестве одного из параметров функции.

Выше можно назвать следующим образом:

In [16]: get_dataset_as_float(intdata, np.min, np.max, np.mean)
Out[16]: (9.0, 87.0, 42.299999)
person deinonychusaur    schedule 11.08.2014