前置基础
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
都可以拿出来单独讲好久,好久,好久……
选择图表
看效果