Почему чтение из массива NumPY так медленно для файла CDF

У меня есть простой файл netCDF, в котором есть куб данных, то есть LAT, LONG, TIME как 3 измерения, и он хранит температуру. Он хранится в форме замаскированного массива в NumPy. Код ниже извлекает его как формат CSV. Но это очень медленно для обработки файла размером 20 МБ, т.е. для каждой итерации требуется 20 секунд, поэтому в целом у меня 4 * 548 * 20 секунд = 43840 секунд = 703 минуты = 12 часов.

Если вы посмотрите на строку с комментарием TAKES_LONG_TIME, это займет больше времени. Я считаю, что для каждой ячейки происходит переключение с кода Python на C в NumPy. Не уверен в следующем сценарии, как я могу решить. Пожалуйста, порекомендуйте. Спасибо.

# conda install -y -c conda-forge iris

import iris
import cf_units as unit
import numpy as np
import datetime
import urllib.request
from os import path


def make_data_object_name(dataset_name, year, month, day, hour, realization, forecast_period):
    template_string = "prods_op_{}_{:02d}{:02d}{:02d}_{:02d}_{:02d}_{:03d}.nc"
    return template_string.format(dataset_name, year, month, day, hour, realization, forecast_period)


def download_data_object(dataset_name, data_object_name):
    url = "https://s3.eu-west-2.amazonaws.com/" + dataset_name + "/" + data_object_name
    urllib.request.urlretrieve(url, data_object_name)  # save in this directory with same name


def load_data():
    filename = 'prods_op_mogreps-uk_20140101_03_02_003.nc'
    if not path.exists(filename):
        # obj_name = make_data_object_name('mogreps-uk', 2014, 1, 1, 3, 2, 3)
        download_data_object('mogreps-uk', filename)

    listofcubes = iris.load(filename)
    air_temps = listofcubes.extract('air_temperature')
    surface_temp = air_temps[0]
    dim_time, dim_lat, dim_long = "time", "grid_latitude", "grid_longitude"

    time_cords = surface_temp.coord(dim_time).points
    time_since = str(surface_temp.coord(dim_time).units)
    lat_cords = surface_temp.coord(dim_lat).points
    long_cords = surface_temp.coord(dim_long).points

    time_records = [str(unit.num2date(time_cords[i], time_since, unit.CALENDAR_STANDARD)) for i in
                    range(len(time_cords))]
    lat_records = [lat_cords[lat_recorded] for lat_recorded in range(len(lat_cords))]
    long_records = [long_cords[long_recorded] for long_recorded in range(len(long_cords))]

    print(len(time_records), len(lat_records), len(long_records))
    print(surface_temp.shape)
    data_size = len(surface_temp.shape)
    print(" File write start -->  ", datetime.datetime.now())
    with open(filename + '.curated', 'w') as filehandle:
        for t, time_record in enumerate(time_records):  # Iterate TIME - 4
            t_a = surface_temp[t] if data_size == 3 else surface_temp[t][0]
            for lat, lat_record in enumerate(lat_records):  # Iterate LAT - 548
                lat_a = t_a[lat]
                iter_start_time = datetime.datetime.now()
                lat_lines = list()
                for lng, long_record in enumerate(long_records):  # Iterate Long 421
                    data = str(lat_a[lng].data.min()) # TAKES_LONG_TIME
                    lat_lines.append(time_record + ',' + str(lat_record) + ',' + str(long_record) + ',' + data + '\n')
                filehandle.writelines(lat_lines)
                print(t, time_record, lat, lat_record, " time taken in seconds -> ",
                      (datetime.datetime.now() - iter_start_time).seconds)



if __name__ == "__main__":
    load_data()



person Dave    schedule 10.08.2019    source источник
comment
чтобы немного уточнить: сколько строк нужно написать в filehandle.writelines(lat_lines)? И из кода я сделал вывод, что lat_a[lng] уже должен быть скаляром, если ваши данные - время x широта x долгота x температура - так зачем вызывать .min() в lat_a[lng].data.min()?   -  person MrFuppes    schedule 17.08.2019


Ответы (1)


Когда вы впервые читаете в кубах с помощью iris.load, фактические массивы данных не загружаются в память (см. Настоящие и отложенные данные в Руководстве пользователя Iris). Поскольку вы разрезаете свой куб перед доступом к subcube.data, фактическая загрузка происходит индивидуально для каждого фрагмента. Итак, вы возвращаетесь и получаете доступ к файлу NetCDF каждый раз, когда выполняется ваша строка «TAKES_LONG_TIME».

Чтобы загрузить все в память перед запуском циклов, просто добавьте строку с надписью

surface_temp.data

Это должно ускорить процесс, но может быть нежелательным в зависимости от того, сколько памяти у вас доступно. Компромисс можно найти, выбрав другой уровень в цикле for для реализации данных (например, ta.data или la.data).

person RuthC    schedule 01.11.2019