Как создавать быстрые и точные диаграммы разброса с большим количеством данных на Python
Диаграммы разброса довольно просты и их легко создать - по крайней мере, я так думал. Недавно мне пришлось визуализировать набор данных с сотнями миллионов точек данных. Если вы разработчик Python, вы сразу же импортируете matplotlib
и приступите к работе. Но оказывается, что есть более эффективные, быстрые и интуитивно понятные способы создания диаграмм рассеивания.
В чем проблема matplotlib
? Что ж, matplotlib
это отличная библиотека Python, и она определенно обязательна для изучения данных. Но matplotlib
это также огромный универсал и может работать неоптимально в некоторых сценариях. Это один из тех.
Давайте предположим, что мы имеем массив X
и его форма (1_000_000, 2)
. Каждый столбец представляет одну ось. Итак, любая строка - это координата
import matplotlib.pylot as plt
plt.scatter(X[:, 0], X[:, 1])
plt.show()
Таким образом, получается диаграмма рассеивания, но мы понятия не имеем, перекрываются ли точки или в целом об интенсивности области. Мы можем исправить это путем установки s
и alpha
параметров. Первый параметр управляет размером каждой точки, второй - непрозрачностью. Нам нужен размер и непрозрачность, которые позволят нам различать разные точки. И здесь начинается метод проб и ошибок. И по мере увеличения размера ваших данных этот процесс становится все более и более болезненным.
import matplotlib.pylot as plt
plt.scatter(X[:, 0], X[:, 1], s=1, alpha=0.1)
plt.show()
Может datashader?
datashader
- отличная библиотека для визуализации больших наборов данных. Основное улучшение связано с процессом растеризации: в matplotlib
будет создан круг для каждой точки данных, а затем, когда вы отображаете свои данные, он должен будет выяснить, какие пиксели на вашем холсте занимает каждая точка. Обычно каждая точка занимает несколько пикселей. datashader
вместо этого вы разделите ваше 2D-пространство на горизонтальные width
и вертикальные height
интервалы. А затем он просто проверяет, какую ячейку занимает каждый образец. Если этот процесс кажется вам знакомым, то это потому, что именно так вы создаете гистограмму. В этом случае 2D-гистограмма с интервалами одинаковой ширины. Гистограмма, которую вы создали, уже имеет ту же форму, что и ваше изображение. Итак, все, что осталось, это применить цветовую карту. Мне очень нравится fire
из библиотеки colorcet
. Обратите внимание, что datashader
только принимает в качестве входных данных DataFrame
(будь то pandas
, dask
или другие) и ваши данные должны быть сохранены как float32
.
Нам нужны дополнительные пакеты:
pip install datashader, colorcet, pandas
import datashader as ds
import pandas as pd
import colorcet as cc
import matplotlib.pyplot as plt
df = pd.DataFrame(data=X, columns=["x", "y"]) # create a DF from array
cvs = ds.Canvas(plot_width=500, plot_height=500) # auto range or provide the `bounds` argument
agg = cvs.points(df, 'x', 'y') # this is the histogram
img = ds.tf.set_background(ds.tf.shade(agg, how="log", cmap=cc.fire), "black").to_pil() # create a rasterized image
plt.imshow(img)
plt.axis('off')
plt.show()
Точки больше не могут частично перекрываться, и, поскольку вы создаете гистограмму, палитра решит вашу предыдущую проблему непрозрачности. Вы можете решить, как вы хотите нормализовать данные (используя параметр how
). Здесь мы устанавливаем его как логарифмический, но обычно я бы рекомендовал оставить его по умолчанию, который использует более сложный метод.
А теперь давайте просто добавим на график цветовую полосу. Фу! Как? Все, что у нас есть, это гистограмма и растровое изображение. Мы можем отображать изображение matplotlib
, но не имеем информации о цветовой карте. Вероятно, есть какой-то хак, но давайте будем честными: это будет не что иное, как грязный хак и может внести много путаницы. Таким образом datashader
, это замечательно, быстро и просто в использовании, но за это приходится платить: никаких цветных полос и интерактивных графиков (т.е. нет графического интерфейса, который позволяет масштабировать, вращать и т.д.). Тем не менее, он действительно хорош при создании внешней графики. Итак, попробуйте! Особенно, когда вы имеете дело с данными геолокации.
Еще более быстрое решение… и цветные полосы!
Для меня динамические графики не так важны, но цветные полосы мне очень нужны. Итак, я пошел дальше и разработал свое собственное решение. На самом деле это действительно просто. Первоначально я использовал numpy
для вычисления 2D-гистограммы, а затем с помощью matplotlib
обрабатывать затенение. Но numpy.histogram2d
работает довольно медленно, поэтому я перешел на fast_histogram
. Я снова использую карту colorcet.fire
, но получаю к ней доступ через cc.cm
, чтобы быть совместимым с matplotlib
. Кроме того, я предлагаю аргумент norm
в пользу использования логарифмической палитры. Имейте в виду vmin=0
, что это недопустимо, потому что логарифм нуля не определен. Вот почему все значения 0 сопоставляются с так называемым bad
цветом. Поэтому просто установите bad
цвет на наименьшее значение (или на любой другой цвет, который вы хотите, чтобы был ваш фон).
Нам нужны дополнительные пакеты:
pip install fast-histogram colorcet
import colorcet as cc
import matplotlib.colors as colors
import matplotlib.pyplot as plt
from fast_histogram import histogram2d
cmap = cc.cm["fire"].copy()
cmap.set_bad(cmap.get_under()) # set the color for 0
bounds = [[X[:, 0].min(), X[:, 0].max()], [X[:, 1].min(), X[:, 1].max()]]
h = histogram2d(X[:, 0], X[:, 1], range=bounds, bins=500)
plt.imshow(h, norm=colors.LogNorm(vmin=1, vmax=h.max()), cmap=cmap)
plt.axis('off')
plt.show()
Обратите внимание, что созданные диаграммы разброса повернуты из-за способа вывода данных fast_histogram
.
Чтобы показать цветовую полосу, просто добавьте plt.colorbar()
раньше plt.show()
.