Skip to content

Commit aa65d80

Browse files
authored
Add support for violin plot (#936)
* Add support for violin plot * Add box plot and mean line option to builder
1 parent 34ef6be commit aa65d80

File tree

4 files changed

+219
-0
lines changed

4 files changed

+219
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package tech.tablesaw.plotly.api;
2+
3+
import tech.tablesaw.api.Table;
4+
import tech.tablesaw.plotly.components.Figure;
5+
import tech.tablesaw.plotly.components.Layout;
6+
import tech.tablesaw.plotly.traces.BoxTrace;
7+
import tech.tablesaw.plotly.traces.ViolinTrace;
8+
9+
public class ViolinPlot {
10+
11+
private static final int HEIGHT = 600;
12+
private static final int WIDTH = 800;
13+
14+
public static Figure create(
15+
String title, Table table, String groupingColumn, String numericColumn, boolean showBoxPlot, boolean showMeanLine) {
16+
Layout layout = Layout.builder().title(title).height(HEIGHT).width(WIDTH).build();
17+
ViolinTrace trace =
18+
ViolinTrace.builder(table.categoricalColumn(groupingColumn), table.nCol(numericColumn))
19+
.boxPlot(showBoxPlot)
20+
.meanLine(showMeanLine)
21+
.build();
22+
return new Figure(layout, trace);
23+
}
24+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package tech.tablesaw.plotly.traces;
2+
3+
import static tech.tablesaw.plotly.Utils.dataAsString;
4+
5+
import com.mitchellbosecke.pebble.error.PebbleException;
6+
import com.mitchellbosecke.pebble.template.PebbleTemplate;
7+
import java.io.IOException;
8+
import java.io.StringWriter;
9+
import java.io.UncheckedIOException;
10+
import java.io.Writer;
11+
import java.util.Map;
12+
import tech.tablesaw.api.CategoricalColumn;
13+
import tech.tablesaw.api.NumericColumn;
14+
15+
public class ViolinTrace extends AbstractTrace {
16+
17+
private final Object[] x;
18+
private final double[] y;
19+
private final boolean showBoxPlot;
20+
private final boolean showMeanLine;
21+
22+
private ViolinTrace(ViolinBuilder builder) {
23+
super(builder);
24+
this.x = builder.x;
25+
this.y = builder.y;
26+
this.showMeanLine = builder.showMeanLine;
27+
this.showBoxPlot = builder.showBoxPlot;
28+
}
29+
30+
public static ViolinBuilder builder(Object[] x, double[] y) {
31+
return new ViolinBuilder(x, y);
32+
}
33+
34+
public static ViolinBuilder builder(CategoricalColumn<?> x, NumericColumn<? extends Number> y) {
35+
return new ViolinBuilder(x, y);
36+
}
37+
38+
public static ViolinBuilder builder(double[] x, double[] y) {
39+
Double[] xObjs = new Double[x.length];
40+
for (int i = 0; i < x.length; i++) {
41+
xObjs[i] = x[i];
42+
}
43+
return new ViolinBuilder(xObjs, y);
44+
}
45+
46+
@Override
47+
public String asJavascript(int i) {
48+
Writer writer = new StringWriter();
49+
PebbleTemplate compiledTemplate;
50+
51+
try {
52+
compiledTemplate = engine.getTemplate("trace_template.html");
53+
compiledTemplate.evaluate(writer, getContext(i));
54+
} catch (PebbleException e) {
55+
throw new IllegalStateException(e);
56+
} catch (IOException e) {
57+
throw new UncheckedIOException(e);
58+
}
59+
return writer.toString();
60+
}
61+
62+
private Map<String, Object> getContext(int i) {
63+
64+
Map<String, Object> context = super.getContext();
65+
context.put("variableName", "trace" + i);
66+
context.put("y", dataAsString(y));
67+
context.put("x", dataAsString(x));
68+
if (showBoxPlot){
69+
context.put("box", "{visible: true}");
70+
}
71+
if (showMeanLine){
72+
context.put("meanLine", "{visible: true}");
73+
}
74+
return context;
75+
}
76+
77+
public static class ViolinBuilder extends TraceBuilder {
78+
79+
private static final String type = "violin";
80+
private final Object[] x;
81+
private final double[] y;
82+
private boolean showBoxPlot;
83+
private boolean showMeanLine;
84+
85+
ViolinBuilder(Object[] x, double[] y) {
86+
this.x = x;
87+
this.y = y;
88+
}
89+
90+
@Override
91+
public ViolinBuilder name(String name) {
92+
super.name(name);
93+
return this;
94+
}
95+
96+
ViolinBuilder(CategoricalColumn<?> x, NumericColumn<? extends Number> y) {
97+
this.x = columnToStringArray(x);
98+
this.y = y.asDoubleArray();
99+
}
100+
101+
public ViolinBuilder boxPlot(boolean show) {
102+
this.showBoxPlot = show;
103+
return this;
104+
}
105+
106+
public ViolinBuilder meanLine(boolean show) {
107+
this.showMeanLine = show;
108+
return this;
109+
}
110+
111+
public ViolinTrace build() {
112+
return new ViolinTrace(this);
113+
}
114+
115+
@Override
116+
public ViolinBuilder xAxis(String xAxis) {
117+
super.xAxis(xAxis);
118+
return this;
119+
}
120+
121+
@Override
122+
public ViolinBuilder yAxis(String yAxis) {
123+
super.yAxis(yAxis);
124+
return this;
125+
}
126+
127+
@Override
128+
protected String getType() {
129+
return type;
130+
}
131+
}
132+
}

jsplot/src/main/resources/trace_template.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,10 @@
8686
{% endif %}
8787
type: '{{type}}',
8888
name: '{{name}}',
89+
{% if box is not null %}
90+
box: {{box}},
91+
{% endif %}
92+
{% if meanLine is not null %}
93+
meanline: {{meanLine}},
94+
{% endif %}
8995
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package tech.tablesaw.plotly;
2+
3+
import static org.junit.jupiter.api.Assertions.assertNotNull;
4+
5+
import org.junit.jupiter.api.Disabled;
6+
import org.junit.jupiter.api.Test;
7+
import tech.tablesaw.plotly.components.Figure;
8+
import tech.tablesaw.plotly.traces.ViolinTrace;
9+
10+
@Disabled
11+
class ViolinTest {
12+
13+
private final Object[] x = {
14+
"sheep",
15+
"cows",
16+
"fish",
17+
"tree sloths",
18+
"sheep",
19+
"cows",
20+
"fish",
21+
"tree sloths",
22+
"sheep",
23+
"cows",
24+
"fish",
25+
"tree sloths"
26+
};
27+
private final double[] y = {1, 4, 9, 16, 3, 6, 8, 8, 2, 4, 7, 11};
28+
29+
@Test
30+
void testAsJavascriptWithBoxPlot() {
31+
ViolinTrace trace = ViolinTrace.builder(x, y).boxPlot(true).build();
32+
assertNotNull(trace.asJavascript(1));
33+
}
34+
35+
@Test
36+
void showWithMeanLine() {
37+
ViolinTrace trace = ViolinTrace.builder(x, y).meanLine(true).build();
38+
Figure figure = new Figure(trace);
39+
assertNotNull(figure);
40+
Plot.show(figure, "target");
41+
}
42+
43+
@Test
44+
void showWithBoxPlot() {
45+
ViolinTrace trace = ViolinTrace.builder(x, y).boxPlot(true).build();
46+
Figure figure = new Figure(trace);
47+
assertNotNull(figure);
48+
Plot.show(figure, "target");
49+
}
50+
51+
/** Test ensures that the name() method returns a ViolinTraceBuilder as expected. */
52+
@Test
53+
void name() {
54+
ViolinTrace trace = ViolinTrace.builder(x, y).name("my name").build();
55+
assertNotNull(trace);
56+
}
57+
}

0 commit comments

Comments
 (0)