Folium + Bokeh: низкая производительность и большое использование памяти
Я использую Folium и Bokeh вместе в блокноте Jupyter. Я перебираю фрейм данных, и для каждой строки вставляю маркер на карту Фолиума, извлекаю некоторые данные из отдельного фрейма данных, создаю диаграмму Боке из этих данных и затем встраиваю диаграмму Боке во всплывающее окно карты Фолиума в IFrame. Код выглядит следующим образом:
map = folium.Map(location=[36.710021, 35.086146],zoom_start=6)
for i in range (0,len(duty_station_totals)):
popup_table = station_dept_totals.loc[station_dept_totals['Duty Station'] == duty_station_totals.iloc[i,0]]
chart = Bar(popup_table,label=CatAttr(columns=['Department / Program'],sort=False),values='dept_totals',
title=duty_station_totals.iloc[i,0] + ' Staff',xlabel='Department / Program',ylabel='Staff',plot_width=350,plot_height=350)
hover = HoverTool(point_policy='follow_mouse')
hover.tooltips=[('Staff','@height'),('Department / Program','@{Department / Program}'),('Duty Station',duty_station_totals.iloc[i,0])]
chart.add_tools(hover)
html = file_html(chart, INLINE, "my plot")
iframe = folium.element.IFrame(html=html, width=400, height=400)
popup = folium.Popup(iframe, max_width=400)
marker = folium.CircleMarker(duty_station_totals.iloc[i,2],
radius=duty_station_totals.iloc[i,1] * 150,
color=duty_station_totals.iloc[i,3],
fill_color=duty_station_totals.iloc[i,3])
marker.add_to(map)
folium.Marker(duty_station_totals.iloc[i,2],icon=folium.Icon(color='black',icon_color=duty_station_totals.iloc[i,3]),popup=popup).add_to(map)
map
Этот цикл работает очень медленно и добавляет прибл. 200 МБ на использование памяти связанного процесса python 3.5 за цикл! Фактически, после запуска цикла пару раз весь мой macbook замедляется до ползучести - даже мышь отстает. Связанная карта также сильно запаздывает при прокрутке и масштабировании, и всплывающие окна открываются медленно. В случае, если это не очевидно, я довольно новичок в мире аналитики Python и веб-визуализации, так что, возможно, здесь есть что-то явно неэффективное.
Мне интересно, почему это так и есть ли лучший способ отображения диаграмм Боке во всплывающих окнах карты. Из некоторых базовых экспериментов, которые я провел, не похоже, что проблема заключается в вызовах Bar
- использование памяти, кажется, действительно стремительно растет, когда я включаю звонки file_html
и только хуже, как звонки folium.element.IFrame
добавлены. Похоже, что происходит утечка памяти из-за увеличения использования памяти при повторном запуске того же кода.
Если у кого-то есть идеи относительно того, как добиться того же эффекта (открытие диаграмм Боке при нажатии на маркер Фолиума) более эффективным способом, я был бы очень признателен!
Обновление после некоторых экспериментов
Я шаг за шагом проходил цикл и наблюдал за изменениями в использовании памяти, поскольку добавлялось больше шагов, чтобы попытаться определить, какой фрагмент кода вызывает эту проблему. На стороне Боке, самый большой виновник, кажется, призывы к file_html()
- при выполнении цикла через этот шаг он добавляет около 5 МБ использования памяти к связанному процессу Python 3.5 за цикл (цикл создает 18 диаграмм), даже если bokeh.io.curdoc().clear()
,
Однако, по-видимому, большая проблема связана с Folium. выполнение всего цикла, включая создание IFrames для Folium с HTML-кодом, сгенерированным Bokeh, и маркеры карты, связанные с IFrames, увеличивают использование памяти процесса Python на 25–30 Мб за цикл.
Итак, я думаю, что это переходит к большему количеству вопросов Фолиума. Почему эта структура так интенсивно использует память и есть ли лучший способ? Кстати, сохранение полученной карты Folium в виде файла HTML с map.save('map.html')
создает огромный, 22 МБ HTML-файл.
1 ответ
Есть много разных вариантов использования, и некоторые из них идут с неизбежными компромиссами. Чтобы сделать некоторые другие варианты использования очень простыми и удобными, Bokeh имеет скрытый "текущий документ" и продолжает накапливать там вещи. Для конкретного случая последовательного создания группы графиков в цикле, вы захотите вызвать bokeh.io.reset_output()
между каждым, чтобы предотвратить это накопление.