Как ускорить matplotlib, построение / рисование и сохранение подзаговоров? - PullRequest
1 голос
/ 24 апреля 2019

Я хочу нарисовать довольно маленький набор данных IoT-CSV, около 2 ГБ.Он имеет следующие размеры (~ 20.000, ~ 18.000).Каждый столбец должен стать частью графика с собственной осью y.Я использую следующий код для генерации картинки:

times = pd.date_range('2012-10-01', periods=2000, freq='2min')
timeseries_array = np.array(times);
cols = random.sample(range(1, 2001), 2000)
values = []
for col in cols:
    values.append(random.sample(range(1,2001), 2000))

time = pd.DataFrame(data=timeseries_array, columns=['date'])
graph = pd.DataFrame(data=values, columns=cols, index=timeseries_array)

fig, axarr = plt.subplots(len(graph.columns), sharex=True, sharey=True, 
constrained_layout=True, figsize=(50,50))
fig.autofmt_xdate()

for i, ax in enumerate(axarr):
    ax.plot(time['date'], graph[graph.columns[i]].values)
    ax.set(ylabel=graph.columns[i])
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    myFmt = mdates.DateFormatter('%d.%m.%Y %H:%M')
    ax.xaxis.set_major_formatter(myFmt)
    ax.label_outer()

print('--save-fig--')
plt.savefig(name, dpi=500)
plt.close()

Но это невероятно медленно, для 100 сюжетов это заняло ~ 1 минуту, для 2000 около 20 минут.Ну, на самом деле моя машина имеет 10 ядер и 35 ГБ оперативной памяти.Есть ли у вас какие-либо намеки на ускорение процесса?Можно ли сделать многопоточность?Как я вижу, это только одно ядро.Есть ли какие-то хитрости, чтобы рисовать только важные вещи?Или есть альтернативный метод, чтобы нарисовать этот график быстрее, все на одной фигуре без подзаговоров?

1 Ответ

1 голос
/ 24 апреля 2019

Благодаря @Asmus я придумал это решение, снизив меня с 20 минут до 40 секунд (2000, 2000).Я не нашел ни одного хорошего, хорошо документированного решения для начинающих, таких как я, поэтому я публикую здесь свое сообщение, которое используется для временных рядов и огромного количества столбцов:

def print_image_fast(name="default.png", graph=[]):
    int_columns = len(graph.columns)
    #enlarge our figure for every 1000 columns by 30 inch, function well with 500 dpi labelsize 2 and linewidth 0.1
    y_size = (int_columns / 1000) * 30
    fig = plt.figure(figsize=(10, y_size))
    ax = fig.add_subplot(1, 1, 1)
    #set_time_formatter for timeseries
    myFmt = mdates.DateFormatter('%d.%m.%Y %H:%M')
    ax.xaxis.set_major_formatter(myFmt)
    #store the label offsets
    y_label_offsets = []
    current = 0
    for i, col in enumerate(graph.columns):
        #last max height of the column before
        last = current
        #current max value of the column and therefore the max height on y
        current = np.amax(graph[col].values)


        if i == 0:
            #y_offset to move the graph along the y axis, starting with column 0 the offset is 0
            y_offset = 0
        else:
            #add the last y_offset (aggregated y_offset from the columns before) + the last offset + 1 is our new Y - zero point to start drawing the new graph
            y_offset = y_offset + last + 1

        #our label offset is always our current y_offset + half of our height (half of current max value)
        y_offset_label = y_offset + (current / 2)
        #append label position to array
        y_label_offsets.append(y_offset_label)
        #plot our graph according to our offset
        ax.plot(graph.index.values, graph[col].values + y_offset,
                'r-o', ms=0.1, mew=0, mfc='r', linewidth=0.1)

    #set boundaries of our chart, last y_offset + full current is our limit for our y-value
    ax.set_ylim([0, y_offset+current])
    #set boundaries for our timeseries, first and last value
    ax.set_xlim([graph.index.values[0], graph.index.values[-1]])

    #print columns with computed positions to y axis
    plt.yticks(y_label_offsets, graph.columns, fontsize=2)
    #print our timelabels on x axis
    plt.xticks(fontsize=15, rotation=90)

    plt.savefig(name, dpi=500)
    plt.close()

// Редактировать: Для всех, кто заинтересован, датафреймс (20k, 20k) polutes моего барана с ~ 20gb.И мне пришлось изменить savefig на svg, потому что Agg не может обрабатывать размеры больше 2 ^ 16 пикселей

...