前置基礎
Flask 是什麼?
Flask 是一個使用 Python 編寫的輕量級 Web 應用框架。
- 官方文檔
- 中文文檔
NumPy 是什麼?
NumPy是Python的一個用於科學計算的基礎包。它提供了多維數組對象,多種衍生的對象(例如隱藏數組和矩陣)和一個用於數組快速運算的混合的程序,包括數學,邏輯,排序,選擇,I/O,離散傅立葉變換,基礎線性代數,基礎統計操作,隨機模擬等等。
- 官網
Pandas 是什麼?
Pandas是一個強大的分析結構化數據的工具集;它的使用基礎是Numpy(提供高性能的矩陣運算);用於數據挖掘和數據分析,同時也提供數據清洗功能。
- 官方文檔
- 中文文檔
Echarts 是什麼?
ECharts 是一款開源的、基於 web 的、跨平臺的支持快速創建交互式可視化的框架,它易於使用、擁有豐富的內置交互以及高性能。ECharts 通過一套聲明式的可視設計語言定製內置的圖表類型,並且底層的流式架構和高性能的圖形渲染器極大地提高了 ECharts 的擴展性和性能。
Echarts 官網
Echarts Multiple Y Axis 圖表
官網 multiple-y-axis 圖表 demo
接下來,我們就是要集成它到我們的 Superset
二次開發 Step-By-Step
使用容器環境安裝 Echarts
docker-compose ps docker-compose exec superset-node bash cd /app/superset-frontend/ npm i echarts
visualizations 文件夾
它位於項目 superset-frontend/src/visualizations
我們在此處新建如下文件夾 EchartsMultipleYAxis
,它包含:
- images (可視化組件展示用的縮略圖)
-
- thumbnail.png
- thumbnailLarge.png
- EchartsMultipleYAxisPlugin.jsx(組件核心邏輯)
- index.js(組件導出)
- options.js(Echarts Multiple Y Axis 圖表官方默認配置項)
- transformProps.js(將 Superset 傳過來的屬性過濾一下)
EchartsMultipleYAxisPlugin.jsx
,為少
這裡簡單的寫了一下(大家可以根據需求自行調整):
..... function EchartsMultipleYAxisPlugin(elem, props) { const { width, height, data, colorScheme, } = props; elem.style.width = width; elem.style.height = height; const echart = echarts.init(elem); let colors = CategoricalColorNamespace.getScale(colorScheme).colors; defaultOptions.color = colors; const legend = []; const xAxisData = []; let xAxisDataFlag = true; let yAxisMaxs = []; defaultOptions.yAxis.forEach((y, index) => { const sd = data[index]; const title = sd.key; y.name = sd.key; y.axisLabel.formatter = `{value}`; y.axisLine.lineStyle.color = colors[index]; const seriesD = []; defaultOptions.series[index].data = []; sd.values.forEach((value) => { xAxisDataFlag && xAxisData.push(value.x); defaultOptions.series[index].data.push((value.y).toFixed(2)); }) defaultOptions.series[index].name = y.name const yAxisMax = Math.max.apply(null, defaultOptions.series[index].data); y.max = Math.ceil(yAxisMax) + 5; yAxisMaxs.push(yAxisMax); xAxisDataFlag = false; defaultOptions.legend.data.push(y.name); }); defaultOptions.xAxis[0].data = xAxisData; echart.setOption(defaultOptions); } ....
進入 presets/MainPreset.js
,加入如下兩句:
...... import EchartsMultipleYAxisChartPlugin from "../EchartsMultipleYAxis"; ...... new EchartsMultipleYAxisChartPlugin().configure({ key: 'echarts_multiple_y_axis' }) ......
explore 文件夾
進入 superset-frontend/src/explore/components/controls/VizTypeControl.jsx
文件
加入 'echarts_multiple_y_axis',排序一下。
const DEFAULT_ORDER = [ ...other, 'echarts_multiple_y_axis' ]
進入 superset-frontend/src/explore/components/controlPanels
文件夾
我們在這裡新建一個文件 EchartsMultipleYAxis.js
,為少
在這裡加上相關配置。
import { t } from '@superset-ui/translation'; export default { label: t('Echarts Multiple Y Axis Chart'), controlPanelSections: [ { label: t('Query'), expanded: true, controlSetRows: [ ['metrics'], ['adhoc_filters'], ['groupby'], ['columns'], ['row_limit'], ['contribution'], ], }, { label: t('Chart Options'), expanded: true, controlSetRows: [ ['color_scheme', 'label_colors'], ], }, ], controlOverrides: { groupby: { label: t('Series'), }, columns: { label: t('Breakdowns'), description: t('Defines how each series is broken down'), }, }, };
setup 文件夾
進入 superset-frontend/src/explore/setupPlugins.ts
,加入如下代碼:
.... .registerValue('echarts_multiple_y_axis', EchartsMultipleYAxis)
用來註冊下這個插件。
viz.py 文件
我們加入如下 python
代碼。
class EchartsMultipleYAxisViz(DistributionPieViz): """Echarts Multiple Y Axis Chart""" viz_type = "echarts_multiple_y_axis" verbose_name = _("Echarts Multiple Y Axis Chart") is_timeseries = False def query_obj(self) -> QueryObjectDict: d = super().query_obj() fd = self.form_data if len(d["groupby"]) < len(fd.get("groupby") or []) + len( fd.get("columns") or [] ): raise QueryObjectValidationError( _("Can't have overlap between Series and Breakdowns") ) if not fd.get("metrics"): raise QueryObjectValidationError(_("Pick at least one metric")) if not fd.get("groupby"): raise QueryObjectValidationError(_("Pick at least one field for [Series]")) return d def get_data(self, df: pd.DataFrame) -> VizData: if df.empty: return None fd = self.form_data metrics = self.metric_labels columns = fd.get("columns") or [] # pandas will throw away nulls when grouping/pivoting, # so we substitute NULL_STRING for any nulls in the necessary columns filled_cols = self.groupby + columns df[filled_cols] = df[filled_cols].fillna(value=NULL_STRING) row = df.groupby(self.groupby).sum()[metrics[0]].copy() row.sort_values(ascending=False, inplace=True) pt = df.pivot_table(index=self.groupby, columns=columns, values=metrics) if fd.get("contribution"): pt = pt.T pt = (pt / pt.sum()).T pt = pt.reindex(row.index) chart_data = [] for name, ys in pt.items(): if pt[name].dtype.kind not in "biufc" or name in self.groupby: continue if isinstance(name, str): series_title = name else: offset = 0 if len(metrics) > 1 else 1 series_title = ", ".join([str(s) for s in name[offset:]]) values = [] for i, v in ys.items(): x = i if isinstance(x, (tuple, list)): x = ", ".join([str(s) for s in x]) else: x = str(x) values.append({"x": x, "y": v}) d = {"key": series_title, "values": values} chart_data.append(d) return chart_data
關於 viz.py 代碼細節
數據科學本身就是複雜的。
NumPy
or Pandas
都可以拿出來單獨講好久,好久,好久……