Задание : получение графиков (обычно комбинация заполненных / незаполненных контуров) некоторых переменных, содержащихся в наборе данных, полученных с помощью численных моделей прогнозирования погоды.Это необходимо автоматизировать, так как новые данные загружаются каждые 6 часов (приблизительно), а новые графики создаются и загружаются на FTP для визуализации как можно скорее.
Мое решение и моя проблема : скрипт драйвера, который заботится о загрузке и подготовке файлов, записан в bash
по соображениям скорости.Поскольку на сервере данные разбиты на отдельные файлы, содержащие один момент времени и один уровень, эта фаза распараллеливается с использованием (gnu) parallel
.Этот этап довольно быстрый и оптимизированный.Однако следующий этап, который включает в себя построение данных с использованием python
и главным образом matplotlib.pyplot
, по-прежнему самый длинный и требует много памяти.
Анатомия сценария построения У меня есть 2 секции, функция main
, где вся предварительная обработка входных файлов выполняется только один раз, чтобы использовать как можно меньше памяти и вычислений, и функция plot_files
, которая отображает только данные.
import xarray as xr
from multiprocessing import Pool
from functools import partial
def main():
file = glob(input_file)
dset = xr.open_dataset(file[0])
# Do some pre-processing and get the variables/coordinates
# from the file
var = dset.var
Затем функция main
охватывает несколько экземпляров функции построения графика с использованием multiprocessing
.Здесь экземпляр фигуры matplotlib
вместе с проекцией basemap
создаются таким образом, что это выполняется только один раз: функция построения графика позаботится о добавлении элементов в график и удалит их на каждом временном шаге для следующей итерации.
fig = plt.figure()
ax = plt.gca()
m, x, y = get_projection(lon2d, lat2d, projection)
# All the arguments that need to be passed to the plotting function
args = dict(m=m, x=x, y=y, ax=ax,
var=var, time=time, projection=projection)
# Parallelize the plotting by dividing into chunks and processes
dates = chunks(time, chunks_size)
plot_files_param = partial(plot_files, **args)
p = Pool(processes)
p.map(plot_files_param, dates)
Распараллеливание во времени выглядело естественным, так как вы должны создать один и тот же график за многократный шаг, но данные уже есть, и вы сможете их использовать!
Функция построения графиков делает как можно меньше: возьмите переменную, создайте график, перекрыв различные уровни (при необходимости), и удалите элементы, когда фигура была экспортирована, чтобы подготовиться к следующему временному шагу.
def plot_files(dates, **args):
for date in dates:
# Find index in the original array to subset when plotting
i = np.argmin(np.abs(date - args['time']))
cs = args['ax'].contourf(args['x'], args['y'], args['var'][i])
plt.savefig(filename, **options_savefig)
remove_collections([cs])
remove_collections
заботится об удалении любого элемента (это могут быть контурные линии, метки, аннотации ...).
В настоящее время у меня работает скрипт для каждого разного типа графика, который я делаю, в то время как все общие функции импортируются из модуля utils.py
.
Я уверен, что это не самая умная реализация параллельной прорисовки.Недостатки, которые я вижу сейчас, - это использование памяти, которое может быть уменьшено путем передачи в plot_files
функции массива только на определенном временном шаге, но я не уверен, что можно распараллелить с multiprocessing
через переменное измерение.
У кого-нибудь есть совет по улучшению моего кода?Я попытался свести примеры к ядру, но, конечно, есть еще много деталей.Один из проектов, включающих этот скрипт, можно посмотреть здесь https://github.com/guidocioni/icon_forecasts