Почему размер файла netCDF4 так сильно отличается от способа записи? - PullRequest
0 голосов
/ 17 апреля 2020

У меня в нескольких текстовых файлах хранятся 2 измерения данных (одинаковой формы) с разным временем и разными группами. Теперь я хочу преобразовать эти данные в один файл netCDF с несколькими группами netCDF. Каждая переменная группы имеет такие же размеры, как: dimensions:{time=62, lat=118, lon=104}. И я пишу данные тремя способами. Коды записываются в python3 .7 и пакете netCDF4.

from netCDF4 import Dataset, date2num, date2index
import numpy as np
import os
from datetime import datetime, timedelta


def initialize(fpath):
    rootgrp = Dataset(fpath, 'w')
    rootgrp.createDimension('time', 62)
    rootgrp.createDimension('lat', 118)
    rootgrp.createDimension('lon', 104)

    times = rootgrp.createVariable('time', 'f8', ('time', ))
    lats = rootgrp.createVariable('lat', 'f4', ('lat', ))
    lons = rootgrp.createVariable('lon', 'f4', ('lon', ))

    lats.units = 'degrees north'
    lons.units = 'degrees east'
    times.units = 'hours since 1900-01-01 00:00:00.0'
    times.calendar = 'gregorian'
    datetimes = [
        datetime(2020, 3, 1, 8) + n * timedelta(hours=12) for n in range(62)
    ]

    lats[:] = np.linspace(-40, 40, 118)
    lons[:] = np.linspace(80, 160, 104)
    times[:] = date2num(datetimes, times.units, times.calendar)
    return rootgrp


def write(fpath, data, **kwargs):
    if not os.path.exists(fpath):
        rootgrp = initialize(fpath)
    else:
        rootgrp = Dataset(fpath, 'r+')

    grppath = kwargs['grppath']
    varname = kwargs['varname']
    grp = rootgrp.createGroup(grppath)
    if varname in grp.variables:
        var = grp.variables[varname]
    else:
        var = grp.createVariable(varname,
                                 'f4', ('time', 'lat', 'lon'),
                                 zlib=True,
                                 least_significant_digit=1)

    times = rootgrp.variables['time']
    datetimes = kwargs.get('datetimes', None)
    if datetimes is None:
        time_index = slice(None)
    else:
        time_index = date2index(datetimes, times, calendar=times.calendar)

    print(var[time_index, :, :].shape)
    print(data.shape)
    var[time_index, :, :] = data
    rootgrp.close()


def get_data(groups, datetimes):
    shape = (118, 104)
    size = shape[0] * shape[1]
    all_group = {}
    for group in groups:
        data_list = []
        for time in datetimes:
            data = np.random.random(size).reshape(shape)
            data_list.append(data)
        all_group[group] = data_list
    return all_group


def way1(dateimes, grouped_data):
    for i, time in enumerate(datetimes):
        for group, data in grouped_data.items():
            write('way1.nc',
                  data[i],
                  grppath=group,
                  varname='random',
                  datetimes=time)


def way2(datetimes, grouped_data):
    for group in grouped_data:
        all_data = np.stack(grouped_data[group])
        write('way2.nc',
              all_data,
              grppath=group,
              varname='random',
              datetimes=datetimes)


def way3(datetimes, grouped_data):
    for group, data in grouped_data.items():
        for i, time in enumerate(datetimes):
            write('way3.nc',
                  data[i],
                  grppath=group,
                  varname='random',
                  datetimes=time)


groups = list('abcdefghijklmnopqrstuvwxyz')
datetimes = [
    datetime(2020, 3, 1, 8) + n * timedelta(hours=12) for n in range(62)
]
grouped_data = get_data(groups, datetimes)
way1(datetimes, grouped_data)
way2(datetimes, grouped_data)
way3(datetimes, grouped_data)

Все файлы, записанные тремя способами, одинаковы (переменная ChunkSizes = (62U, 118U, 104U)), за исключением размера файла.

путь 1: 495 324 392 байта (503,3 МБ диска)

путь 2: 15 608 108 байтов (16,7 МБ диска)

путь 3: 15 608 108 байтов (16,7 МБ диска)

Мне интересно, может ли кто-нибудь объяснить мне. Спасибо!

1 Ответ

0 голосов
/ 20 апреля 2020

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

В вашей программе вы проверяете, существует ли файл или переменная netCDF, а затем создаете его только в том случае, если еще не существует Однако вы не проверяете группы, вы всегда создаете их. Изменяя grp = rootgrp.createGroup(grppath) на следующие строки, размер way1.nc уменьшается до 19 МБ.

if grppath in rootgrp.groups:
    grp = rootgrp[grppath]
else:
    grp = rootgrp.createGroup(grppath)

При удалении объекта из файла HDF5 размер файла остается прежним (см. Раздел 5.5.2 удаление набора данных из файла и восстановление пространства в руководстве пользователя HDF5 ). Поэтому я подозреваю, что создание группы с одним и тем же именем снова и снова будет выделять пространство для хранения, но не освобождает дисковое пространство старой группы, что приводит к утечке памяти. Я не знаю, почему это происходит только способом 1, а не способом 3.

Также я пока не понимаю, почему way1.nc все еще немного больше (19 МБ), чем другие (15 МБ) ).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...