-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Waterfall Chart Visualization Operator (#2825)
This pull request introduces a new plot visualizer, which is the **Waterfall Chart** operator. The Waterfall Chart visualizes sequential data where each category (step) contributes to a cumulative effect, showing increases or decreases. The waterfall chart helps users analyze how different factors contribute to the total value. Users can specify an X attribute (categories or steps), Y attribute (numerical values for each step). **Screenshot:** ![Waterfall](https://github.com/user-attachments/assets/5ad96e7b-e50b-4995-89b6-2181e1feeb28) --------- Authored-by: seongjinyoon <[email protected]> --------- Co-authored-by: Seongjin <[email protected]>
- Loading branch information
1 parent
2b3e779
commit d397133
Showing
3 changed files
with
93 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
.../edu/uci/ics/texera/workflow/operators/visualization/waterfallChart/WaterfallOpDesc.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package edu.uci.ics.texera.workflow.operators.visualization.waterfallChart | ||
|
||
import com.fasterxml.jackson.annotation.{JsonProperty, JsonPropertyDescription} | ||
import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle | ||
import edu.uci.ics.texera.workflow.common.metadata.{OperatorGroupConstants, OperatorInfo} | ||
import edu.uci.ics.texera.workflow.common.operators.PythonOperatorDescriptor | ||
import edu.uci.ics.texera.workflow.common.tuple.schema.{Attribute, AttributeType, Schema} | ||
import edu.uci.ics.amber.engine.common.workflow.{InputPort, OutputPort} | ||
import edu.uci.ics.texera.workflow.operators.visualization.{ | ||
VisualizationConstants, | ||
VisualizationOperator | ||
} | ||
import edu.uci.ics.texera.workflow.common.metadata.annotations.AutofillAttributeName | ||
|
||
class WaterfallChartOpDesc extends VisualizationOperator with PythonOperatorDescriptor { | ||
|
||
@JsonProperty(value = "xColumn", required = true) | ||
@JsonSchemaTitle("X Axis Values") | ||
@JsonPropertyDescription("The column representing categories or stages") | ||
@AutofillAttributeName | ||
var xColumn: String = _ | ||
|
||
@JsonProperty(value = "yColumn", required = true) | ||
@JsonSchemaTitle("Y Axis Values") | ||
@JsonPropertyDescription("The column representing numeric values for each stage") | ||
@AutofillAttributeName | ||
var yColumn: String = _ | ||
|
||
override def getOutputSchema(schemas: Array[Schema]): Schema = { | ||
Schema.builder().add(new Attribute("html-content", AttributeType.STRING)).build() | ||
} | ||
|
||
override def operatorInfo: OperatorInfo = | ||
OperatorInfo( | ||
"Waterfall Chart", | ||
"Visualize data as a waterfall chart", | ||
OperatorGroupConstants.VISUALIZATION_GROUP, | ||
inputPorts = List(InputPort()), | ||
outputPorts = List(OutputPort()) | ||
) | ||
|
||
def createPlotlyFigure(): String = { | ||
s""" | ||
| x_values = table['$xColumn'] | ||
| y_values = table['$yColumn'] | ||
| | ||
| fig = go.Figure(go.Waterfall( | ||
| name="Waterfall", orientation="v", | ||
| measure=["relative"] * (len(y_values) - 1) + ["total"], | ||
| x=x_values, | ||
| y=y_values, | ||
| textposition="outside", | ||
| text=[f"{v:+}" for v in y_values], | ||
| connector={"line": {"color": "rgb(63, 63, 63)"}} | ||
| )) | ||
| | ||
| fig.update_layout(showlegend=True, waterfallgap=0.3) | ||
|""".stripMargin | ||
} | ||
|
||
override def generatePythonCode(): String = { | ||
val finalCode = | ||
s""" | ||
|from pytexera import * | ||
| | ||
|import plotly.graph_objects as go | ||
|import plotly.io | ||
| | ||
|class ProcessTableOperator(UDFTableOperator): | ||
| | ||
| # Generate custom error message as html string | ||
| def render_error(self, error_msg) -> str: | ||
| return '''<h1>Waterfall chart is not available.</h1> | ||
| <p>Reason is: {} </p> | ||
| '''.format(error_msg) | ||
| | ||
| @overrides | ||
| def process_table(self, table: Table, port: int) -> Iterator[Optional[TableLike]]: | ||
| if table.empty: | ||
| yield {'html-content': self.render_error("input table is empty.")} | ||
| return | ||
| ${createPlotlyFigure()} | ||
| html = plotly.io.to_html(fig, include_plotlyjs='cdn', auto_play=False) | ||
| yield {'html-content': html} | ||
|""".stripMargin | ||
finalCode | ||
} | ||
|
||
// Specify the chart type as HTML visualization | ||
override def chartType(): String = VisualizationConstants.HTML_VIZ | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.