swharden / QuickPlot

Experimental Plotting Library for .NET
MIT License
33 stars 6 forks source link

second Y axis #12

Closed swharden closed 5 years ago

swharden commented 5 years ago

I'd like to be able to create a chart like the one below.

image

StendProg commented 5 years ago

Color map for vertical axises looks ideal in this example. But what if there are 5 plots for main axis, and 5 for secondary. How to understand which graph on which axis?

swharden commented 5 years ago

How does the user know which graph is on which axis?

@StendProg this is a good question! The user can control plottable object colors and also axis colors. It will be the user's responsibility to color-code their data appropriately.

image

StendProg commented 5 years ago

Axis can be rainbow color mapped. From top to bottom with plot colors. But it may be hard to implement. Numbers filled with 2 different colors may looks ugly. Not familar with graphic editors to easy test it..

swharden commented 5 years ago

Not familiar with graphic editors to test these ideas

I'm familiar with graphics editors! I'll make some example plots using different color schemes and post the examples here so we can look at them and see which color styles look best.

swharden commented 5 years ago

I'll make some example plots using different color schemes

I started doing this but decided to take a step back and see how other graphing libraries solve this problem. Rather than invent a new rainbow-based method, I think we should use the "principle of least astonishment" and choose a conventional method. Two primary methods are commonly used:

I have decided to develop the second option. We can always come back to this topic later and add the first option too.

Matplotlib Example (5 datasets per axis)

Multi-axis plots with 5 datasets per axis looks ugly because it is bad figure design (not because it is a limitation of the graphing library). Users are encouraged not to do this with any plotting library.

import matplotlib.pyplot as plt
import numpy as np

def randomData():
    return np.cumsum(np.random.random_sample(100)-.5)

plt.figure(figsize=(6, 4))

axis1 = plt.gca()
axis1.set_ylabel("primary Y", color='b', fontsize=16)

for i in range(5):
    axis1.plot(randomData(), color='b')

axis2 = axis1.twinx()
axis2.set_ylabel("secondary Y", color='r', fontsize=16)
for i in range(5):
    axis2.plot(randomData()*1000, color='r')

plt.title("Multi-Y Demo", fontsize=24)
plt.tight_layout()
plt.savefig("multi-y.png")
plt.show()

multi-y

Multi-Y Examples (from Google)

GraphPad

image

Matplotlib

image image

NI DIAdem (?)

image

Excel

image

Mendix

image

MatLab

image

JPGraph

image

OriginLab

image

swharden commented 5 years ago

@StendProg I think this is looking pretty good! What do you think?

shared3

Define second axis when setting-up the plot

https://github.com/swharden/QuickPlot/blob/83bb510a1659412f0ec02fd0fe328a77d4a52bf2/demos/WinFormsDemos/FormMultiY.cs#L37-L40

Control second axis

https://github.com/swharden/QuickPlot/blob/83bb510a1659412f0ec02fd0fe328a77d4a52bf2/demos/WinFormsDemos/FormMultiY.cs#L47 https://github.com/swharden/QuickPlot/blob/83bb510a1659412f0ec02fd0fe328a77d4a52bf2/demos/WinFormsDemos/FormMultiY.cs#L53

StendProg commented 5 years ago

Maybe i am wrong, but i expected different result. During zoom i think it need to zoom both plots, and redraw both axises. In you demo zoom affected only primary plot and axis. Secondary stay AutoAxis all the time.

Also it would be nice to add some param(or default behavior). Then it enabled axis labels filled with plot color automatic, not but manual setup.

Setting secondary axis flag in Style param also a bit confusing. I think Style only about fonts and colors before taking deeper look.

As part of constructive criticism, you done great job anyway.

StendProg commented 5 years ago

I prefer setup secondary axis with some of this interface:

What would you think?

swharden commented 5 years ago

The mouse should pan/zoom both Y axes at the same time

You're right. I checked, and this is how matplotlib does it too.

When data is plotted on the second axis, it would be nice if the second axis would get colored the same color as the data automatically

I like this idea too! I'll do this.

Setting secondary axis flag in Style param also a bit confusing. I think Style only about fonts and colors before taking deeper look. What about Scatter(dataX, data2, style2, secondaryY: true);?

Scatter plots will have customization options like color, linewidth, marker size, marker shape, anti-aliasing, secondary axis, errorbars, and maybe more. My hope is that all these options can be packaged into the Style object. My goal is to avoid having a large number of arguments.

ScottPlot's argument list got out of control: Plot.cs#L277-L289

Ideally all the plotting functions would just take the data and the config object.

Maybe it would be more clear if I called it DataConfig?

This still needs some work to figure out the best way to proceed 🤔

StendProg commented 5 years ago

Config or settings would be good, Style is confusing name, and i think not only for me. Style class can be moved inside like Settings.Style

swharden commented 5 years ago

Maybe we should make two objects, one for config and one for style?

figure.plot.Scatter(double[] xs, double[] ys, DataConfig config, DataStyle style)
StendProg commented 5 years ago

Take you own decission on api. I can add only one thing, people don't like to go deep in specs, and want to get best result with simple params. If Style would be use frequent, it can be made separate param, otherwise 1 param object better

swharden commented 5 years ago

I modified the user controls so the mouse now controls both Y axes at the same time.

I'll come back around later and improve the style system.

swharden commented 5 years ago

following-up, argument-rich methods are used now and colors of axis labels and ticks are easy to configure:

double[] xs = QuickPlot.Generate.Consecutative(100);
double[] ysSmall = QuickPlot.Generate.Sin(100, mult: .01);
double[] ysBig = QuickPlot.Generate.Cos(100, mult: 100);

var figure = new QuickPlot.Figure();
figure.plot.Scatter(xs, ysSmall, color: SkiaSharp.SKColors.Blue);
figure.plot.Scatter(xs, ysBig, secondY: true, color: SkiaSharp.SKColors.Red);

figure.plot.YLabel("Primary Y", color: SkiaSharp.SKColors.Blue);
figure.plot.YLabel("Secondary Y", secondY: true, color: SkiaSharp.SKColors.Red);
figure.plot.XLabel("Horizontal Axis");
figure.plot.Title("Twin Y Axis Demo");

image