Як і чому я використовував Plotly (замість D3) для візуалізації своїх даних Lollapalooza

D3.js - це чудова бібліотека JavaScript, але вона має дуже круту криву навчання. Це робить завдання побудови цінної візуалізації тим, що може зайняти багато зусиль. Ці додаткові зусилля - це нормально, якщо ваша мета - створити нові та креативні візуалізації даних, але часто це не так.

Часто вашою метою може бути просто побудувати інтерактивну візуалізацію за допомогою відомих діаграм . І якщо ви не інженер-розробник, це може стати трохи складним завданням.

Як науковці з питань обробки даних, одним з наших головних завдань є маніпулювання даними. Сьогодні основним інструментом, який я використовую для цього, є Pandas (Python). Що, якщо я скажу вам, що ви можете створити кілька красивих та інтерактивних діаграм для Інтернету прямо з ваших фреймів даних Pandas ? Ну ти можеш! Для цього ми можемо використовувати Plotly .

Для запису є також бібліотеки Plotly API для Matlab, R та JavaScript, але ми дотримуватимемось бібліотеки Python тут.

Чесно кажучи, Plotly побудований поверх d3.js (і stack.gl). Головна відмінність між D3 та Plotly полягає в тому, що Plotly - це спеціально бібліотека діаграм .

Давайте побудуємо гістограму, щоб дізнатись, як працює Plotly.

Побудова гістограми за допомогою завзято

У філософії Плотлі є 3 основні концепції:

  • Дані
  • Макет
  • Малюнок

Дані

Об'єкт Data визначає, що ми хочемо відображати на діаграмі (тобто дані). Ми визначаємо колекцію даних та специфікації для відображення їх у вигляді сліду . Об’єкт даних може мати багато слідів. Подумайте про лінійну діаграму з двома рядками, що представляють дві різні категорії: кожен рядок - це слід.

Макет

Об'єкт Layout визначає функції, які не пов'язані з даними (наприклад, заголовок, заголовки осей тощо). Ми також можемо використовувати макет для додавання анотацій та фігур на діаграму.

Малюнок

Об'єкт "Фігура" створює кінцевий об'єкт, який планується побудувати. Це об’єкт, який містить як дані, так і макет.

Графічна візуалізація побудована за допомогою plotly.js. Це означає, що API Python - це лише пакет для взаємодії з бібліотекою plotly.js . plotly.graph_objsМодуль містить функції , які будуть генерувати об'єкти графа для нас.

Добре, тепер ми готові побудувати гістограму:

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_type = df.pivot_table( index = "place", columns = "date", values = "price", aggfunc = "sum" ).fillna(0)
trace_microbar = go.Bar( x = df_purchases_by_type.columns, y = df_purchases_by_type.loc["MICROBAR"])
data = [trace_microbar]
layout = go.Layout(title = "Purchases by place", showlegend = True)
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

Примітка: у цій статті ми не будемо говорити про те, що я роблю з фреймами даних. Але якщо вам потрібна публікація про це, повідомте мене в коментарях?

Гаразд, отже, спочатку ми хочемо показати бари однієї категорії (місце називається "MICROBAR"). Отже, ми створюємо об’єкт даних (список) із go.Bar()(трасуванням) із зазначенням даних для осей x та y. Trace - це словник, а data - це список словників. Ось trace_microbarвміст (зверніть увагу на ключ типу):

{'type': 'bar', 'x': Index(['23/03/2018', '24/03/2018', '25/03/2018'], dtype="object", name="date"), 'y': date 23/03/2018 0.0 24/03/2018 0.0 25/03/2018 56.0 Name: MICROBAR, dtype: float64}

В об’єкті Layout ми встановлюємо заголовок діаграми та параметр showlegend. Потім ми обертаємо Дані та Макет у малюнок та викликаємо plotly.offline.plot()для відображення діаграми. Plotly має різні варіанти відображення діаграм, але давайте дотримуватися опції офлайн тут. Це відкриє вікно браузера з нашою діаграмою.

Я хочу відобразити все на стовпчастій діаграмі, тому ми створимо список даних із усіма слідами (місцями), які ми хочемо відобразити, і встановимо barmodeпараметр на стек .

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_place = df.pivot_table(index="place",columns="date",values="price",aggfunc="sum").fillna(0)
data = []
for index,place in df_purchases_by_place.iterrows(): trace = go.Bar( x = df_purchases_by_place.columns, y = place, name=index ) data.append(trace)
layout = go.Layout(, showlegend=True, barmode="stack" )
figure = go.Figure(data=data, layout=layout)
offline.plot(figure)

І це основи Plotly. Щоб налаштувати наші діаграми, ми встановлюємо різні параметри для трасування та макета. А тепер давайте поговоримо про візуалізацію Лоллапалузи.

Мій досвід Lollapalooza

Для випуску Lollapalooza Brazil в 2018 році всі покупки були зроблені через браслет із підтримкою RFID. Вони надсилають дані на вашу електронну адресу, тому я вирішив поглянути на них. Що ми можемо дізнатись про мене та мій досвід, аналізуючи покупки, зроблені мною на фестивалі?

Ось як виглядають дані:

  • Дата покупки
  • година придбання
  • продукту
  • кількість
  • етап
  • місце, де я робив покупку

На основі цих даних давайте відповімо на деякі запитання.

Куди я поїхав під час фестивалю?

Дані повідомляють лише назву місця, де я зробив покупку, а фестиваль відбувся в Autódromo de Interlagos. Я взяв карту з етапами звідси і скористався інструментом геореференції з georeference.com, щоб отримати координати широти та довготи для етапів.

We need to display a map and the markers for each purchase, so we will use Mapbox and the scattermapbox trace. First let’s plot only the stages to see how this works:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pd
mapbox_token = "" #//www.mapbox.com/help/define-access-token/
df = pd.read_csv("stages.csv")
trace = go.Scattermapbox( lat = df["latitude"], lon = df["longitude"], text=df["stage"], marker=go.Marker(size=10), mode="markers+text", textposition="top" )
data = [trace]
layout = go.Layout( mapbox=dict( accesstoken=mapbox_token, center=dict( lat = -23.701057, lon = -46.6970635 ), zoom=14.5 ) )
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

Let’s learn a new Layout parameter: updatemenus. We will use this to display the markers by date. There are four possible update methods:

  • "restyle": modify data or data attributes
  • "relayout": modify layout attributes
  • "update": modify data and layout attributes
  • "animate": start or pause an animation)

To update the markers, we only need to modify the data, so we will use the "restyle" method. When restyling you can set the changes for each trace or for all traces. Here we set each trace to be visible only when the user changes the dropdown menu option:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pdimport numpy as np
mapbox_token = ""
df = pd.read_csv("data.csv")
df_markers = df.groupby(["latitude","longitude","date"]).agg(dict(product = lambda x: "%s" % ", ".join(x), hour = lambda x: "%s" % ", ".join(x)))df_markers.reset_index(inplace=True)
data = []update_buttons = []
dates = np.unique(df_markers["date"])
for i,date in enumerate(dates): df_markers_date = df_markers[df_markers["date"] == date] trace = go.Scattermapbox( lat = df_markers_date["latitude"], lon = df_markers_date["longitude"], name = date, text=df_markers_date["product"]+"

"+df_markers_date["hour"], visible=False ) data.append(trace)

 visible_traces = np.full(len(dates), False) visible_traces[i] = True
 button = dict( label=date, method="restyle", args=[dict(visible = visible_traces)] ) update_buttons.append(button)
updatemenus = [dict(active=-1, buttons = update_buttons)]
layout = go.Layout( mapbox=dict( accesstoken=mapbox_token, center=dict( lat = -23.701057, lon = -46.6970635), zoom=14.5), updatemenus=updatemenus )
figure = go.Figure(data = data, layout = layout)
offline.plot(figure)

How did I spend my money?

To answer that, I created a bar chart with my spendings for food and beverage by each day and built a heatmap to show when I bought stuff. We already saw how to build a bar chart, so now let’s build a heatmap chart:

import plotly.graph_objs as goimport pandas as pdimport plotly.offline as offline
df = pd.read_csv("data.csv")
df_purchases_by_type = df.pivot_table(index="place",columns="date",values="price",aggfunc="sum").fillna(0)df["hour_int"] = pd.to_datetime(df["hour"], format="%H:%M", errors="coerce").apply(lambda x: int(x.hour))
df_heatmap = df.pivot_table(index="date",values="price",columns="hour", aggfunc="sum").fillna(0)
trace_heatmap = go.Heatmap( x = df_heatmap.columns, y = df_heatmap.index, z = [df_heatmap.iloc[0], df_heatmap.iloc[1], df_heatmap.iloc[2]] )
data = [trace_heatmap]
layout = go.Layout(title="Purchases by place", showlegend=True)
figure = go.Figure(data=data, layout=layout)
offline.plot(figure)

Which concerts did I watch?

Now let’s go to the coolest part: could I guess the concerts I attended based only on my purchases?

Ideally, when we are watching a show, we are watching the show (and not buying stuff), so the purchases should be made before or after each concert. I then made a list of each concert happening one hour before, one hour after, and according to the time the purchase was made.

Щоб дізнатись, на якому з цих шоу я був, я розрахував відстань від місця покупки до кожного етапу. Шоу, на яких я був, повинні мати найменшу відстань до концесій.

Оскільки ми хочемо показати кожну точку даних, найкращим вибором для візуалізації є таблиця. Побудуємо один:

import plotly.graph_objs as goimport plotly.offline as offlineimport pandas as pd
df_table = pd.read_csv("concerts_I_attended.csv")
def colorFont(x): if x == "Yes": return "rgb(0,0,9)" else: return "rgb(178,178,178)"
df_table["color"] = df_table["correct"].apply(lambda x: colorFont(x))
trace_table = go.Table( header=dict( values=["Concert","Date","Correct?"], fill=dict( color=("rgb(82,187,47)")) ), cells=dict( values= [df_table.concert,df_table.date,df_table.correct], font=dict(color=([df_table.color]))) )
data = [trace_table]
figure = go.Figure(data = data)
offline.plot(figure)

Три концерти були відсутні, а чотири були неправильними, що дало нам точність 67% та відкликання 72%.

Склавши все це разом: тире

У нас є всі діаграми, але мета полягає в тому, щоб зібрати їх усіх на сторінці. Для цього ми будемо використовувати Dash (від Plotly).

«Dash - це фреймворк Python для створення аналітичних веб-додатків. JavaScript не потрібен. Dash ідеально підходить для створення додатків для візуалізації даних із дуже спеціальними користувальницькими інтерфейсами на чистому Python. Це особливо підходить для тих, хто працює з даними на Python ". - Сайт Plotly

Dash is written on top of Flask, Plotly.js, and React.js. It works in a very similar way to the way we create Plotly charts:

import dashimport dash_core_components as dccimport dash_html_components as htmlimport plotly.graph_objs as goimport pandas as pd app = dash.Dash()
df_table = pd.read_csv("concerts_I_attended.csv").dropna(subset=["concert"])def colorFont(x): if x == "Yes": return "rgb(0,0,9)" else: return "rgb(178,178,178)"
df_table["color"] = df_table["correct"].apply(lambda x: colorFont(x))
trace_table = go.Table(header=dict(values=["Concert","Date","Correct?"],fill=dict(color=("rgb(82,187,47)"))),cells=dict(values=[df_table.concert,df_table.date,df_table.correct],font=dict(color=([df_table.color]))))
data_table = [trace_table]
app.layout = html.Div(children=[ html.Div( [ dcc.Markdown( """ ## My experience at Lollapalooza Brazil 2018 *** """.replace(' ', ''), className="eight columns offset-by-two" ) ], className="row", style=dict(textAlign="center",marginBottom="15px") ),
html.Div([ html.Div([ html.H5('Which concerts did I attend?', style=dict(textAlign="center")), html.Div('People usually buy things before or after a concert, so I took the list of concerts, got the distances from the location of the purchases to the stages and tried to guess which concerts did I attend. 8 concerts were correct and 3 were missing from a total of 12 concerts.', style=dict(textAlign="center")), dcc.Graph(id='table', figure=go.Figure(data=data_table,layout=go.Layout(margin=dict(t=30)))), ], className="twelve columns"), ], className="row")])
app.css.append_css({ 'external_url': '//codepen.io/chriddyp/pen/bWLwgP.css'})
if __name__ == '__main__': app.run_server(debug=True)

Cool right?

I hosted the final visualization here and the all the code is here.

There are some alternatives to hosting the visualizations: Dash has a public dash app hosting and Plotly also provides a web-service for hosting graphs.

Did you found this article helpful? I try my best to write a deep dive article each month, you can receive an email when I publish a new one.

I had a pretty good experience with Plotly, I’ll definitely use it for my next project. What are your thoughts about it after this overview? And what other tools do you use to build visualizations for the web? Share them in the comments! And thank you for reading! ?