بصفتي Senior consultant data scientist، أُدرك جيّدًا أهمّية تلخيص عملي في Dashboards وApps.
هذا يُتيح لي تعميم خوارزمياتي وعملي ووضعها في رسومات بديهية لفهم أفضل وأسرع. في هذا المقال، سنتطرّق إلى واحدة من أشهر الأدوات ومكتبات python المستخدمة في تصميم dashboards وapplications.
الملخّص كالتالي:
- Dash by Plotly
- Streamlit
- Bokeh
- Elastic stack
- نشر Heroku
Dash by Plotly
Dash أداة مفتوحة المصدر طوّرتها Plotly. تسمح بإدراج عدّة أنواع من widgets مع إمكانية اختيار التوزيع والنمط لأنّ تخطيط dash مبني على HTML ويسمح بتنسيق CSS.
يتمّ الإعداد بتشغيل سطر الأوامر التالي:
pip install dash
pip install dash_core_components
pip install dash_html_componentslayout لتطبيق dash هو شجرة هرمية من المكوّنات، مزيج بين عناصر HTML ورسومات مبنية باستخدام:
- مكتبة dash_html_components توفّر فئات لجميع علامات HTML، وتصف الـ keyword arguments سمات HTML مثل style وclassName وid. باستخدام هذه المكتبة، يمكننا إضافة عناصر HTML مثل
Div, H1, P,... etc. لمزيد من التفاصيل، أنصحك بفحص الوثائق. - مكتبة dash_core_components تُولّد مكوّنات أعلى مستوى مثل عناصر التحكّم والرسومات وتستخدم بنية Plotly. على سبيل المثال، يمكننا إدراج
Button, Dropdown, Slider,DatePickerRange, ... etc.. لمزيد من التفاصيل، يُرجى زيارة الموقع الرسمي.
يمكن تطوير تطبيق Dash بسيط باستخدام أسطر Python التالية:
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
app.title='Hello World'
app.layout = html.Div(children=[
html.H1(children='Hello Dash!', style={'textAlign':
'center', 'color': '#7FDBFF'}),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(
id='example-graph',
figure={
'data': [
{'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar',
'name': 'SF'},
{'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar',
'name': u'Montréal'},
],
'layout': {
'title': 'Dash Data Visualization'
}
}
),
])
if __name__ == '__main__':
app.run_server(debug=True)يُتيح خيار debug=True أخذ التعديلات الأخيرة بعين الاعتبار دون الحاجة إلى إعادة تشغيل التطبيق.
لتشغيل التطبيق، نستخدم سطر الأوامر التالي:
python app_file.pyتحصل على التطبيق التالي الذي يعمل على http://127.0.0.1:8050/:

التنسيق
من الممكن إضافة ملفّ CSS يستبدل النمط الأساسي للتطبيق، ممّا يسمح لك بتخصيص رسوماتك وعناصر HTML. للقيام بذلك، يجب أن تكون بنية المجلّد كالتالي:
app.py
assets/
|-- style.css
|-- custom-script.js
|-- img.png
|-- background.jpg
|-- favicon.icoمن الإلزامي وضع ملفّ CSS وجميع الصور الأخرى المُدرَجة في مجلّد يُسمّى assets ليُقرَأ من قِبَل Dash. بمجرّد الانتهاء من ذلك، كلّ ما علينا فعله هو تحديد style sheet باستخدام أسطر Python التالية:
external_stylesheets=["assets/style.css"]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)Bootstrap
عند بناء dash-app، أُفضّل شخصيًا العمل مع Bootstrap. وهي مكتبة Python لـ Plotly Dash، تحتوي على عناصر منسّقة ومتقدّمة.
يتمّ الإعداد باستخدام سطر الأوامر التالي:
pip install dash-bootstrap-componentsمن المهمّ تضمين موضوع bootstrap كأحد style sheets:
external_stylesheets=["assets/style.css", dbc.themes.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)يحتوي Boostrap على العديد من العناصر مثل DropDownMenu, Navbar, Progress, Button, ... etc. أحد أكثر العناصر فائدة هو Layout، فهو يسمح بهيكلة dashboard باستخدام Rows وColumns وهو مفيد جدًّا لبنية التطبيق.
لمزيد من التفاصيل، لا تتردّد في زيارة الوثائق الرسمية.
Callbacks
كأيّ تطبيق آخر، من المهمّ تمكين التفاعلية بين الرسومات وذلك باستخدام دوال callback.
كلّ عنصر مُدرَج له ميزتان مهمّتان
- id: تسمية فريدة للعنصر
- properties: تختلف من عنصر إلى آخر
ربط عنصرَين في تطبيق يتمّ على النحو التالي:

في هذه الحالة الخاصّة، يؤثّر العنصر الأوّل على الثاني: property_1 للعنصر الأوّل المُختار من خلال id_1 يؤثّر على property_2 للعنصر الثاني الذي يتمّ الوصول إليه من خلال id_2.
from dash.dependencies import Input, Output
@app.callback(
Output(component_id='id_2', component_property='property_2'),
[Input(component_id='id_1', component_property='property_1')]
)
def callback_function(input_value):
"""
What to do
"""ملاحظة: من الممكن أخذ العديد من Inputs في دالّة callback.
Cache
عند تطوير تطبيق، من المهمّ جدًّا أن يكون لديك عناصر في Cache من أجل تسهيل التحميل وتسريع التفاعلية، Cache يسمح بذلك.
لا يدعم Dash ميزة cache محدّدة، لكن إحدى الطرق للتغلّب على هذه المشكلة هي استخدام global variables في Python.
Tabs
يمكن إدراج Tabs في Dash باستخدام مكوّن dcc.Tab، حيث تكون عناصره هي العناصر المكوّنة لـ tab.
mport dash_html_components as html
import dash_core_components as dcc
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
dcc.Tabs([
dcc.Tab(label='1st Tab', children=[
dcc.Graph(),
###
]),
dcc.Tab(label='2nd Tab', children=[
dcc.Graph(),
###
]),
dcc.Tab(label='3rd Tab', children=[
dcc.Graph(),
###
]),
])
])المعرض
لدى Dash معرض حيث يمكنك تصفّح واكتشاف dashboards أخرى، فهو نقطة انطلاق جيّدة ومصدر إلهام.
Streamlit
Streamlit إطار عمل تطبيقات مفتوح المصدر طُوّر لإنشاء dashboards ML، في الغالب، بطريقة سريعة وفعّالة باستخدام Python. الإعداد بسيط جدًّا ويمكن إجراؤه باستخدام سطر الأوامر التالي:
pip install streamlitمقارنةً بـ Dash، Streamlit لديه معالجة أفضل لـ Cache، كما هو مطوّر في الأقسام التالية، وبالتالي يسمح بتكرارات تطوير أسرع باستخدام العملية التالية:

بمجرّد إنشاء ملفّك، يمكنك كتابة سطر الأوامر هذا الذي سيُشغّل تطبيقك على http://localhost:8501
streamlit run file.pyكما في Dash، من الممكن اختيار الوضع Always re-run للحصول على التعديلات مباشرة.
يحتوي معرض widgets الخاصّ بـ Streamlit على عدّة عناصر يمكن إدراجها جميعًا باستخدام سطر شيفرة واحد: Markdown، SelectBox، Slider، Plot، إلخ... لاحظ أنّه يمكن إدراج widgets إمّا في one-page area أو على sidebar وأنّ الإدراج يتمّ باتّباع vertical stacking:
import streamlit as st
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn import datasets
from sklearn.decomposition import PCA
import pandas as pd
img=mpimg.imread('imgs/streamlite_funct.png')
iris = datasets.load_iris()
feature_names=['sepal length (cm)',
'sepal width (cm)',
'petal length (cm)',
'petal width (cm)']
database=pd.DataFrame(iris.data, columns=feature_names)
database["class"]=iris.target
# plot the first three PCA dimensions
fig = plt.figure(1, figsize=(8, 6))
ax = Axes3D(fig, elev=-150, azim=110)
X_reduced = PCA(n_components=3).fit_transform(iris.data)
ax.scatter(X_reduced[:, 0], X_reduced[:, 1], X_reduced[:, 2], c=iris.target,
cmap=plt.cm.Set1, edgecolor='k', s=40)
ax.set_title("First three PCA directions")
ax.set_xlabel("1st eigenvector")
ax.w_xaxis.set_ticklabels([])
ax.set_ylabel("2nd eigenvector")
ax.w_yaxis.set_ticklabels([])
ax.set_zlabel("3rd eigenvector")
ax.w_zaxis.set_ticklabels([])
def main():
########### Sidebar ##############################
st.sidebar.markdown("# Hello World")
st.sidebar.selectbox('Select a tool', ["Dash", "Kibana", "Streamlit", "Bokeh"])
st.markdown("## Multiselection")
st.multiselect('Select a tool', ["Dash", "Kibana", "Streamlit", "Bokeh"])
st.markdown("## Radio buttons")
st.radio('Select a tool', ["Dash", "Kibana", "Streamlit", "Bokeh"])
st.markdown("## Slider")
st.slider('Select a Number', min_value=1, max_value=4, value=1)
st.markdown("## Image")
st.image(img, width=500)
st.markdown("## DataBase")
st.dataframe(database)
st.markdown("## Plot")
st.write(fig)
if __name__ == "__main__":
main()نحصل على التطبيق التالي:

الدالّة st.write مهمّة جدًّا ويمكن استخدامها لإدراج العديد من عناصر Python. لمزيد من التفاصيل، أنصحك بقراءة وثائق streamlit.
التفاعلية: يمكن تعيين كلّ عنصر لمتغيّر يمثّل قيمته الحالية، ثمّ يمكن استخدامه لتحديث عنصر آخر ومن ثَمَّ التفاعلية.
Cache
يُستخدم Cache عادةً لتخزين البيانات الثابتة التي لا تتغيّر بغضّ النظر عن الاختيارات التي تتمّ على التطبيق. قد نُعرّف في كثير من الأحيان دوال في التطبيق ليست هناك حاجة لإعادة تشغيلها في كلّ مرّة يُحدَّث فيها التطبيق، يمكننا تخزينها مؤقّتًا باستخدام شيفرة Python التالية:
@st.cache
def function_1():
####Tabs
في العديد من التطبيقات، يمكن أن تكون Tabs مهمّة جدًّا: إحدى الطرق للقيام بذلك في Streamlit هي إضافة selectbox في sidebar الذي سيؤثّر اختياره على بنية dashboard وذلك باستخدام عبارات if على قيمة الصندوق.
st.sidebar.markdown("Tabs")
tab=st.sidebar.selectbox('Select a Tab', ["Home", "Documentation", "Partners", "Contact"])
if tab=="Home":
#Develop the one_pager
##
elif tab=="Documentation":
###
elif tab=="Partners":
###
elif tab=="Contact":
###
Health app, Streamlit
عندما بدأت اكتشاف Streamlit، قرّرت استخدامه بتطوير تطبيق صحّي بسيط يسمح بمعالجة الأمر مباشرة.

يمكنك زيارة التطبيق باستخدام هذا الرابط، السكربت مُستضاف في هذا المستودع. لمزيد من التطبيقات، يمكنك زيارة المعرض الرسمي.
Bokeh
Bokeh مكتبة Python تسمح بإنشاء dashboard تفاعلي أيضًا. يمكن تثبيتها باستخدام سطر الأوامر التالي:
pip install bokehالتطبيق أدناه، مثلًا، يمكن إنشاؤه باستخدام سكربت Python التالي:

from bokeh.io import show, curdoc
from bokeh.plotting import figure, ColumnDataSource
from bokeh.layouts import row, column
from bokeh.models.widgets import Tabs, Panel
from bokeh.models import Slider, CheckboxGroup, RadioGroup, Button, CustomJS
#data source
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
#Plots
plot_1 = figure(plot_width=400, plot_height=400)
plot_1.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
plot_2=figure(tools='box_zoom, lasso_select')
plot_2.circle(x=[1,2,3,4,5], y=[2,3,1,1,2])
plot_2.background_fill_color = 'black'
plot_3=figure(tools='box_zoom, lasso_select')
plot_3.circle(x=[1,2,3,4,5], y=[2,3,1,1,2])
#widgets
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
button = Button(label='Click')
checkbox = CheckboxGroup(labels=['Kibana', 'Bokeh', 'Streamlit', 'Dash'])
radio = RadioGroup(labels=['Kibana', 'Bokeh', 'Streamlit', 'Dash'])
#Interactivity
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
var f = cb_obj.value
var x = data['x']
var y = data['y']
for (var i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.change.emit();
""")
slider.js_on_change('value', callback)
#Plots zoom-linking
plot_2.x_range=plot_3.x_range
plot_2.y_range=plot_3.y_range
#Tabs
first=Panel(child=row(column(slider, button, checkbox, radio), plot_1), title='first')
second=Panel(child=row(plot_2,plot_3), title='second')
tabs=Tabs(tabs=[first, second])
#App
curdoc().add_root(tabs)
show(tabs)لاحظ أنّه بمجرّد تعريف الشكل plot، نستخدم بنية plot.graph لتعيين الرسم المرغوب فيه. تُعالَج البيانات باستخدام ColumnDataSource وLayout باستخدام row, column. نُخصّص widgets المُضافة، Slider, CheckboxGroup, RadioGroup, Button، ونربطها بالرسومات باستخدام JS callback CustomJS. لتشغيل تطبيقك على http://localhost:5006/myapp، يمكنك استخدام سطر الأوامر التالي:
bokeh serve --show myapp.pyالمعرض
كجميع الأدوات الأخرى، لدى Bokeh معرض غني جدًّا للبدء به.
Elastic stack
Elasticsearch
Elasticsearch نوع خاصّ من فهرسة الجداول يوفّر محرّك بحث أسرع. يمكننا تحويل قاعدة بيانات pandas إلى Elasticbase ثمّ تصوّرها باستخدام Kibana وهي أداة التصوّر للـ stack Elastic. ستحتاج، أوّلًا، إلى تنزيل Kibana وElasticsearch بحسب نظام التشغيل الخاصّ بك.
بمجرّد التثبيت، سنرغب في فهرسة pandas dataframe الخاصّ بنا بشكل صحيح باستخدام elasticsearch. للقيام بذلك، نحتاج أوّلًا إلى إطلاق كلتا خدمتَي elasticsearch وkibana باستخدام أسطر الأوامر:
./elasticsearch-7.6.1/bin/elasticsearch
./kibana-7.6.1/bin/kibanaبمجرّد إطلاق Kibana، يمكننا استخدام سكربت Python هذا لإرسال البيانات إلى خدمات Elasticsearch
import pandas as pd
from elasticsearch import Elasticsearch
# Database loading and service openning
database=pd.read_excel("data/test_db.xlsx")
es_client = Elasticsearch(http_compress=True)
#Elasticsearch does not accept NAN values
print(database.isna().sum().sum())
df=database.copy()
INDEX="laposte" #Its name in Elasticsearch (laposte for example)
TYPE= "record"
def rec_to_actions(df):
import json
for record in df.to_dict(orient="records"):
yield ('{ "index" : { "_index" : "%s", "_type" : "%s" }}'% (INDEX, TYPE))
yield (json.dumps(record, default=int))
e = Elasticsearch()
r = e.bulk(rec_to_actions(df))
#Verify if everything went fine
print(not r["errors"])Kibana
تُطلق خدمة kibana في local host على المنفذ 5601، يمكننا زيارتها في متصفّح باستخدام العنوان localhost:5601. بمجرّد الوصول إلى الصفحة الرئيسية، يمكننا النقر على Dashboard وCreate dashboard على النحو التالي:

يمكننا الآن إضافة widgets عبر هذه الخطوات:

يمكن وضع الرسم المُدرَج باستخدام المؤشّر ممّا يجعل التخطيط سهل التخصيص. لاحظ وجود شريط بحث حيث تُفهرس البيانات باستخدام Elasticsearch، وهذا يجعل الاستعلام أقلّ استهلاكًا للوقت ويُضيف مزيدًا من التفاعلية إلى dashboard، خصوصًا عند التعامل مع قواعد بيانات كبيرة. التفاعلية تُعالَج تلقائيًا في kibana.
Tabs
يمكن إنشاء Tabs في Kibana باستخدام روابط مدمجة في widgets markdowns. أنشئ أوّلًا جميع tabs كلّ واحد في dashboard مختلف ثمّ أضف في كلّ واحد الروابط المدمجة للآخرين.
المعرض
لا تتردّد في زيارة معرض kibana الرسمي.
نشر Heroku
بمجرّد تطوير تطبيقك، يمكنك استضافته على الإنترنت بحيث يمكن الوصول إليه من قِبَل أيّ شخص باستخدام Url. إحدى الطرق للقيام بذلك هي استخدام Heroku الذي يُقدّم هذه الخدمة المجّانية مع بعض القيود.
تحتاج أوّلًا إلى التسجيل ثمّ إنشاء تطبيق heroku عبر الإنترنت سيُربط بمستودع git الخاصّ بك. يجب أن يكون للمستودع البنية التالية:
.
├── app.py
├── requirements.txt
├── setup.sh
└── Procfileيمكنك التحقّق من مستودع git الخاصّ بي لمزيد من المعلومات. على مجلّد git المحلّي، شغّل أسطر الأوامر التالية:
heroku create
git push heroku master
heroku ps:scale web=1
heroku openسيكون تطبيقك في حالة استعداد دائم لكلّ push تقوم به في git الخاصّ بك من أجل أخذ آخر التغييرات بعين الاعتبار.
الخاتمة
التطبيقات وdashboards مرحلة مهمّة وحاسمة جدًّا في كلّ مشروع، فهي تسمح بتلخيص عملنا وجعله أكثر سهولة للمستخدم عبر واجهة بديهية. يعتمد استخدام كلّ تقنية بشكل رئيسي على التسليم والمواعيد النهائية الخاصّة بك: بعض الأدوات تسمح بمرونة أكبر، والبعض الآخر أسرع في التطوير:

من وجهة نظري الشخصية، إذا تمّ استيفاء جميع الشروط، يمكن أن يكون Dash الخيار الأفضل للذهاب معه.
