Если вы пытались визуализировать pandas.DataFrame
раньше, то вы, вероятно, сталкивались с Pandas .plot() API. Эти команды используют Matplotlib для рендеринга статических PNG или SVG в Jupyter блокнотах с использованием встроенного бэкэнда или интерактивных графиков через %matplotlib widget
.
API-интерфейс Pandas .plot()
стал де-факто стандартом для высокоуровневого построения графиков в Python и теперь поддерживается множеством различных библиотек, которые используют набор базовых механизмов построения графиков для обеспечения дополнительных возможностей. Библиотеки, которые в настоящее время поддерживают этот API, включают:
В этом блокноте мы исследуем возможности стандартного API .plot
и продемонстрируем дополнительные возможности, предоставляемые .hvplot
, которые включают бесшовную интерактивность в развернутых информационных панелях и рендеринг на стороне сервера больших наборов данных.
Чтобы показать эти особенности, мы будем использовать набор данных в виде таблиц о землетрясениях и других запрошенных сейсмологических событиях из Каталога землетрясений USGS, используя его API. Конечно, этот набор данных является всего лишь примером; тот же подход можно использовать практически с любым табличным набором данных, и аналогичные подходы можно использовать с наборами данных с координатной привязкой (многомерный массив).
Для работы с пакетом hvplot понадобится настроить программное окружение (установить множество модулей).
Я предпочитаю работать с miniconda и раздельными виртуальными средами.
Далее в командной строке для настройки среды окружения необходимо выполнить:
conda create --name holoviz
conda activate holoviz
conda install anaconda-project
anaconda-project download pyviz/holoviz_tutorial
cd holoviz_tutorial
anaconda-project run jupyter lab
После процесса установки всех необходимых модулей и запуска Jupyter Lab можно открыть оригинал данного блокнота: tutorial/02_Plotting.ipynb
.
Здесь мы сосредоточимся на Pandas, но аналогичный подход будет работать для любого поддерживаемого типа DataFrame, включая Dask для распределенных вычислений или RAPIDS cuDF для вычислений на GPU. Этот набор данных относительно велик (2,1 млн строк), но он все равно должен уместиться в памяти на любой современной машине и, следовательно, не потребует специальных внепроцессорных или распределенных подходов, таких как Dask.
import pandas as pd
!wget https://www.dropbox.com/s/m2r388lpoo7isu9/earthquakes-projected.parq
df = pd.read_parquet('earthquakes-projected.parq')
df.time = df.time.astype('datetime64[ns]')
df = df.set_index(df.time)
print(df.shape)
df.head()
(2116537, 25)
index | depth | depthError | dmin | gap | horizontalError | id | latitude | locationSource | longitude | ... | net | nst | place | rms | status | time | type | updated | easting | northing | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
time | |||||||||||||||||||||
2000-01-31 23:52:00.619 | 0 | 7.800 | 1.400 | 0.09500 | 245.14 | NaN | nn00001936 | 37.1623 | nn | -116.6037 | ... | nn | 5.0 | Nevada | 0.0519 | reviewed | 2000-01-31 23:52:00.619 | earthquake | 2018-04-24T22:22:44.135Z | -1.298026e+07 | 4.461754e+06 |
2000-01-31 23:44:54.060 | 1 | 4.516 | 0.479 | 0.05131 | 52.50 | NaN | ci9137218 | 34.3610 | ci | -116.1440 | ... | ci | 0.0 | 26km NNW of Twentynine Palms, California | 0.1300 | reviewed | 2000-01-31 23:44:54.060 | earthquake | 2016-02-17T11:53:52.643Z | -1.292909e+07 | 4.077379e+06 |
2000-01-31 23:28:38.420 | 2 | 33.000 | NaN | NaN | NaN | NaN | usp0009mwt | 10.6930 | trn | -61.1620 | ... | us | NaN | Trinidad, Trinidad and Tobago | NaN | reviewed | 2000-01-31 23:28:38.420 | earthquake | 2014-11-07T01:09:23.016Z | -6.808523e+06 | 1.197310e+06 |
2000-01-31 23:05:22.010 | 3 | 33.000 | NaN | NaN | NaN | NaN | usp0009mws | -1.2030 | us | -80.7160 | ... | us | NaN | near the coast of Ecuador | 0.6000 | reviewed | 2000-01-31 23:05:22.010 | earthquake | 2014-11-07T01:09:23.014Z | -8.985264e+06 | -1.339272e+05 |
2000-01-31 22:56:50.996 | 4 | 7.200 | 0.900 | 0.11100 | 202.61 | NaN | nn00001935 | 38.7860 | nn | -119.6409 | ... | nn | 5.0 | Nevada | 0.0715 | reviewed | 2000-01-31 22:56:50.996 | earthquake | 2018-04-24T22:22:44.054Z | -1.331836e+07 | 4.691064e+06 |
5 rows × 25 columns
Чтобы сравнить подходы HoloViz с другими, мы возьмем подвыборку (1%) из большого набора данных для дальнейшей обработки любым инструментом:
small_df = df.sample(frac=.01)
print(small_df.shape)
small_df.head()
(21165, 25)
index | depth | depthError | dmin | gap | horizontalError | id | latitude | locationSource | longitude | ... | net | nst | place | rms | status | time | type | updated | easting | northing | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
time | |||||||||||||||||||||
2010-01-30 04:15:07.412 | 537 | 8.700 | 0.10 | NaN | NaN | NaN | ak0101doldjp | 61.576900 | ak | -147.822800 | ... | ak | NaN | Southern Alaska | 0.6700 | reviewed | 2010-01-30 04:15:07.412 | earthquake | 2018-07-06T19:53:39.618Z | -1.645556e+07 | 8.759508e+06 |
2016-01-17 15:31:10.240 | 4984 | 13.000 | 3.00 | 0.33500 | 215.64 | NaN | nn00527303 | 39.901400 | nn | -119.026900 | ... | nn | 9.0 | 37km NNE of Fernley, Nevada | 0.1462 | reviewed | 2016-01-17 15:31:10.240 | earthquake | 2018-07-02T17:46:02.559Z | -1.325001e+07 | 4.851624e+06 |
2005-04-30 01:55:42.980 | 318 | 10.424 | 2.03 | 0.16400 | 122.00 | 0.39 | nc21454257 | 41.020000 | nc | -121.554333 | ... | nc | 11.0 | Northern California | 0.0700 | reviewed | 2005-04-30 01:55:42.980 | earthquake | 2017-01-11T03:26:06.953Z | -1.353137e+07 | 5.015292e+06 |
2007-01-14 13:05:52.210 | 4415 | -0.498 | 3.11 | 0.07117 | 95.00 | 0.90 | nc71006801 | 38.613833 | nc | -122.326667 | ... | nc | 11.0 | Northern California | 0.2600 | reviewed | 2007-01-14 13:05:52.210 | earthquake | 2017-01-16T03:08:12.875Z | -1.361734e+07 | 4.666506e+06 |
2006-10-17 08:18:54.620 | 4072 | 13.200 | 58.50 | NaN | 238.20 | NaN | usp000evcp | -5.923000 | us | 151.013000 | ... | us | 12.0 | New Britain region, Papua New Guinea | 0.5000 | reviewed | 2006-10-17 08:18:54.620 | earthquake | 2014-11-07T01:30:35.061Z | 1.681069e+07 | -6.605228e+05 |
5 rows × 25 columns
Мы будем переключаться между small_df
и df
в зависимости от того, работает ли метод, который мы показываем, только для небольших наборов данных, или его можно использовать для любого набора.
.plot()
¶Первое, что мы хотели бы сделать с этими данными, - это визуализировать места с землетрясениями. Итак, мы хотели бы построить диаграмму рассеяния, где x - долгота, а y - широта.
Мы можем это сделать для небольшого фрейма данных, используя API pandas.plot
и Matplotlib:
%matplotlib inline
small_df.plot.scatter(x='longitude', y='latitude');
Попробуйте заменить inline
на widget
и посмотрите, какие интерактивные возможности доступны в Matplotlib. В некоторых случаях вам может потребоваться перезагрузить страницу и перезапустить блокнот, чтобы она отображалась правильно.
.hvplot
¶Как вы могли увидеть выше, Pandas API легко строит график, где вы можете посмотреть структуру краев тектонических плит, которые во многих случаях соответствуют визуальным краям континентов (например, западная сторона Африки, в центре). Вы можете создать очень похожий график с теми же аргументами, используя hvplot, после импорта hvplot.pandas
для поддержки hvPlot в Pandas:
import hvplot.pandas # noqa: adds hvplot method to pandas objects
small_df.hvplot.scatter(x='longitude', y='latitude')
Здесь, в отличие от Pandas .plot()
, есть действие по умолчанию при наведении курсора на точки данных, чтобы показать значения местоположения, и вы всегда можете панорамировать и масштабировать, чтобы сосредоточиться на любой конкретной области интересующих данных. Масштабирование и панорамирование также работают, если вы используете бэкэнд Matplotlib widget
.
Вы могли заметить, что многие точки в только что созданном графике лежат друг на друге. Это называется "overplotting", и его можно избежать разными способами, например, сделав точки слегка прозрачными или объединяя данные.
Попробуйте изменить alpha
, установив значение 0.1 на графике выше, чтобы увидеть эффект этого подхода.
# Решение здесь
Попробуйте создать график hexbin
.
Как можно узнать о ключевом аргументе alpha
в первом упражнении или как вы можете узнать обо всех опциях, доступных с hvplot
. Для этого вы можете использовать завершение табуляции в Jupyter блокноте или функцию hvplot.help
, которые описаны в руководстве пользователя.
Для завершения табуляции вы можете нажать табуляцию после открывающей скобки в вызове obj.hvplot.<kind>(
. Например, вы можете попробовать нажать табуляцию после частичного выражения small_df.hvplot.scatter(<TAB>
.
Кроме того, вы можете вызвать hvplot.help(<kind>)
, чтобы увидеть всплывающую панель документации в блокноте.
Попробуйте раскомментировать следующую строку и выполнить ее:
# hvplot.help('scatter')
Вы увидите, что есть много вариантов! Вы можете контролировать, какой раздел документации просматриваете, с помощью логических переключателей generic
,docstring
и style
, также задокументированных в руководстве пользователя. Если вы запустите следующую ячейку, вы увидите, что alpha
указана в 'Style options'.
# hvplot.help('scatter', style=True, generic=False)
Эти параметры стиля относятся к параметрам, которые являются частью Bokeh API. Это означает, что ключевое слово alpha
передается непосредственно в Bokeh, как и все другие стилевые параметры. Поскольку это параметры уровня Bokeh, вы можете узнать больше, воспользовавшись функцией поиска в документации Bokeh.
Часто приходится производить выбор еще до того, как вы понимаете свойства данных, например, выбор alpha-значения или размера ячейки для агрегирования. Такие предположения могут склонить вас к определенным аспектам данных, и, конечно же, необходимость выбросить 99% данных может скрыть закономерности, которые вы могли бы увидеть в ином случае. Для первоначального исследования нового набора данных гораздо безопаснее, если вы можете просто просмотреть данные, прежде чем делать какие-либо предположения о его форме или структуре, и без необходимости подвыборки.
Чтобы избежать некоторых проблем традиционных диаграмм рассеяния, мы можем использовать поддержку Datashader. Datashader объединяет данные в каждый пиксель без каких-либо произвольных настроек параметров, делая ваши данные видимыми немедленно, прежде чем вы узнаете, чего от них ожидать. В hvplot
мы можем активировать эту возможность, установив rasterize=True
для вызова Datashader перед рендерингом и cnorm='eq_hist'
("выравнивание гистограммы"), чтобы указать, что цветовое отображение должно адаптироваться к любому распределению данных:
small_df.hvplot.scatter(x='longitude',
y='latitude',
rasterize=True,
cnorm='eq_hist')
Мы уже можем видеть гораздо больше деталей, но помните, что мы все еще наносим на график только 1% данных (21 тыс. землетрясений). С помощью Datashader мы можем быстро и легко построить полный исходный набор данных о 2,1 млн землетрясений:
df.hvplot.scatter(x='longitude',
y='latitude',
rasterize=True,
cnorm='eq_hist',
dynspread=True)
Здесь вы можете увидеть все подробности из миллионов мест землетрясений. Если у вас запущен блокнот, вы можете увеличивать масштаб и видеть дополнительные детали на каждом уровне масштабирования без настройки каких-либо параметров или каких-либо предположений о форме или структуре данных.
Вы можете указать цветовое отображение cnorm='log'
или значение по умолчанию cnorm='linear'
, которые легче интерпретировать, но хорошей практикой является cnorm='eq_hist'
, чтобы увидеть форму данных, прежде чем перейти к более простой для интерпретации, но потенциально скрывающей данные цветовой карте.
Вы можете узнать больше о Datashader на datashader.org или на странице Datashader на holoviews.org. На данный момент самое важное, что нужно знать об этом, это то, что Datashader позволяет нам удобно работать с произвольно большими наборами данных в веб-браузере.
Выберите подмножество данных, например только magitude >5 и нанесите их на другую цветовую карту (допустимые значения cmap
включают 'viridis_r', 'Reds' и 'magma_r'):
# Решение здесь
Давайте углубимся в некоторые другие возможности .plot()
и .hvplot()
, начиная с частоты землетрясений разной магнитуды.
Величина | Эффект землетрясения | Расчетное количество каждый год |
---|---|---|
2,5 или меньше | Обычно не ощущается, но может быть зафиксировано сейсмографом. | 900,000 |
от 2,5 до 5,4 | Часто ощущается, но вызывает лишь незначительные повреждения. | 30,000 |
от 5,5 до 6,0 | Незначительные повреждения зданий и других построек. | 500 |
от 6,1 до 6,9 | Может нанести большой ущерб густонаселенным районам. | 100 |
от 7,0 до 7,9 | Сильное землетрясение. Серьезный ущерб. | 20 |
8.0 или выше | Великое землетрясение. Может полностью разрушить сообщества вблизи эпицентра. | Один раз в 5–10 лет |
В качестве первого прохода мы будем использовать гистограмму сначала с .plot.hist
, затем с .hvplot.hist
. Перед построением графика мы можем очистить данные, заменив любую величину меньше 0 на NaN.
cleaned_df = df.copy()
cleaned_df['mag'] = df.mag.where(df.mag > 0)
cleaned_df.plot.hist(y='mag', bins=50);
df.hvplot.hist(y='mag',
bin_range=(0, 10),
bins=50)
Создайте график ядерной оценки плотности (kde) величины для cleaned_df
:
# Решение здесь
Далее мы классифицируем землетрясения по глубине. Вы можете прочитать обо всех переменных, доступных в этом наборе данных здесь. Согласно странице USGS о глубинах землетрясений, типичная глубина по категориям:
Класс глубины | Глубина |
---|---|
мелкий | 0 - 70 км |
средний | 70 - 300 км |
глубокий | 300 - 700 км |
Сначала мы воспользуемся pd.cut
, чтобы разделить small_dataset
на категории глубины.
import numpy as np
import pandas as pd
depth_bins = [-np.inf, 70, 300, np.inf]
depth_names = ['Shallow', 'Intermediate', 'Deep']
depth_class_column = pd.cut(cleaned_df['depth'], depth_bins, labels=depth_names)
cleaned_df.insert(1,
'depth_class',
depth_class_column)
Теперь мы можем использовать новую категориальную переменную для группировки данных. Сначала мы наложим все группы на один и тот же график, используя опцию by
:
cleaned_df.hvplot.hist(y='mag',
by='depth_class',
alpha=0.6)
ПРИМЕЧАНИЕ: Нажмите на легенду, чтобы отключить определенные категории и посмотреть, что за ними скрывается.
Добавьте subplots=True
и width=300
, чтобы увидеть разные классы рядом, а не наложенными. Оси будут связаны, поэтому попробуйте увеличить.
# Решение здесь
Что, если вам нужен один график, но вы хотите увидеть каждый класс отдельно? Вы можете использовать опцию groupby
, чтобы получить виджет для переключения между классами, здесь, на двумерном графике (использование подмножества данных в качестве двумерных графиков может быть дорогостоящим для вычисления):
cleaned_small_df = cleaned_df.sample(frac=.01)
cleaned_small_df.hvplot.bivariate(x='mag',
y='depth',
groupby='depth_class')
Помимо классификации по глубине, мы можем классифицировать по величине.
Класс магнитуды | Величина |
---|---|
Great | 8 or more |
Major | 7 - 7.9 |
Strong | 6 - 6.9 |
Moderate | 5 - 5.9 |
Light | 4 - 4.9 |
Minor | 3 -3.9 |
classified_df = df[df.mag >= 3].copy()
depth_class = pd.cut(classified_df.depth, depth_bins, labels=depth_names)
classified_df['depth_class'] = depth_class
mag_bins = [2.9, 3.9, 4.9, 5.9, 6.9, 7.9, 10]
mag_names = ['Minor', 'Light', 'Moderate', 'Strong', 'Major', 'Great']
mag_class = pd.cut(classified_df.mag, mag_bins, labels=mag_names)
classified_df['mag_class'] = mag_class
categorical_df = classified_df.groupby(['mag_class', 'depth_class']).count()
Теперь, когда мы разделили данные на две категории, мы можем использовать логарифмическую тепловую карту, чтобы визуально представить эти данные как количество обнаруженных землетрясений в каждой комбинации классов глубины и магнитуды:
categorical_df.hvplot.heatmap(x='mag_class',
y='depth_class',
C='id',
logz=True,
clim=(1, np.nan))
Как видите, hvPlot упрощает интерактивное исследование данных с помощью команд, основанных на широко используемом API Pandas .plot ()
, но теперь поддерживает гораздо больше функций и различные типы данных. Приведенные выше визуализации касаются лишь поверхности того, что доступно на hvPlot, и вы можете изучить веб-сайт hvPlot, чтобы увидеть гораздо больше, или просто изучить его самостоятельно, используя завершение табуляции (df.hvplot.
[TAB]).