Как и защо използвах 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 дефинира функции, които не са свързани с данни (като заглавие, заглавия на оста и т.н.). Също така можем да използваме 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 е речник, а данните - списък с речници. Ето 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. След това обвиваме Data и Layout във фигура и се обаждаме 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

За изданието на Lollapalooza от Бразилия за 2018 г. всички покупки са направени чрез RFID-активирана гривна. Те изпращат данните на вашия имейл адрес, затова реших да ги разгледам. Какво можем да научим за мен и моя опит, като анализираме покупките, които направих на фестивала?

Ето как изглеждат данните:

  • Дата на закупуване
  • час за покупка
  • продукт
  • количество
  • сцена
  • място, където направих покупката

Въз основа на тези данни, нека отговорим на някои въпроси.

Къде отидох по време на фестивала?

Данните ни казват само името на мястото, където направих покупката, а фестивалът се проведе в Autódromo de Interlagos. Взех картата с етапите оттук и използвах инструмента за геореференция от georeference.com, за да получа координатите за географска ширина и дължина за етапите.

Трябва да покажем карта и маркерите за всяка покупка, така че ще използваме Mapbox и scattermapboxпроследяването. Първо нека начертаем само етапите, за да видим как работи това:

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)

Нека се научат нов параметър Разпределение: updatemenus. Ще използваме това, за да покажем маркерите по дата. Има четири възможни метода за актуализация:

  • "restyle": промяна на данни или атрибути на данни
  • "relayout": промяна на атрибутите на оформлението
  • "update": промяна на атрибутите на данни и оформление
  • "animate": стартиране или пауза на анимация)

За да актуализираме маркерите, трябва само да модифицираме данните, така че ще използваме "restyle"метода. При рестайлинг можете да зададете промените за всяка следа или за всички следи. Тук настройваме всяка проследяване да бъде видима само когато потребителят промени опцията от падащото меню:

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)

Как похарчих парите си?

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! ?