Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A multitude of questions using violinplot #59

Open
EitNickS opened this issue Dec 9, 2022 · 11 comments
Open

A multitude of questions using violinplot #59

EitNickS opened this issue Dec 9, 2022 · 11 comments

Comments

@EitNickS
Copy link

EitNickS commented Dec 9, 2022

First, I'd like to say thank you @bastibe for such fantastic code, with this I can visualize some very neat tendencies in cells.

However, I have a three questions regarding the visualization of this code. Please see the attached google drive link for screenshot, and .mat files to help you recreate this figure: https://drive.google.com/drive/folders/1YVO8TfBp95419H410nwd54tnPrHMVAXY?usp=sharing

Below is the screenshot by itself. The code is simple:

hold on
yyaxis left
violinplot(thisData,catnames_labels,'ViolinAlpha', {0.3 0.3}, 'HalfViolin','left', 'ShowMedian', true, 'QuartileStyle','shadow', 'DataStyle', 'scatter', 'MarkerSize', 16);
ylabel('t_{1/2} (seconds)', 'fontsize',...
20, 'Interpreter', 'tex')

yyaxis right
violinplot(thatData,catnames_labels,'ViolinAlpha', {0.3 0.3}, 'HalfViolin','right', 'ShowMedian', true, 'QuartileStyle','shadow', 'DataStyle', 'scatter', 'MarkerSize', 16);
ylim([0, 2.5])
ylabel('Normalized Fluorescence Intensity', 'fontsize',...
20, 'Interpreter', 'tex')
hold off

Screen Shot 2022-12-09 at 1 40 05 PM

  1. Why do the marked quartiles for either half of each violin plot show as diagonal lines? I would think that the quartile's end point would be a single number such as 35, making the line have a slope of 0 @ y=35.

  2. Additionally, while I saw that you showed an offset using the Violin function, and not violinplot, I could not get violin to work, as I would get an error code of incorrect data type, bracketed data is not supported (but even using the example MPG, Origin would result in the same error using Violin). I also couldn't get Violin to plot more than one graph at a time, whereas violinplot was significantly easier to use. So, I was wondering, is there a way to fudge a tiny spacing in violinplot to get a small gap between each half, to be able to look at the data with a finer tooth comb?

  3. The last thing is there appears to be unique marker types surrounding each of the plots, but I'm not sure why those exterior triangles are being plotted (the middle of the 3 has the most prominent markers), and I am not sure what it signifies other than the outer boundary. If all it is is the outer boundaries, can we remove these specific marker types?

Thanks,

Nick

@bastibe
Copy link
Owner

bastibe commented Dec 12, 2022

  1. The marked quartiles shouldn't be diagonal. That must be an indexing error in Violin.m:297. It seems that only the left/right case is affected, which probably means that the variable halfViol is too short or too long. Sadly, I don't have access to Matlab any longer, so I can't directly debug this issue. It would be a great help if you could have a look, and provide a fix in a pull request!
  2. You could create your Violins using violinplot, and then modify the x positions of each component. All plot components are properties on the Violin. Alternatively, you could plot multiple half-Violins at positions of your choosing. Have a look at the code in violinplot to get an idea of how to format the data for Violin.
  3. This seems to be a bug as well. I am a bit puzzled by this, as this is something that should have happened to other people as well, yet I have never seen this before. Has the default plotting behavior changed in a recent Matlab version, or did you change something about your default plot settings?

@EitNickS
Copy link
Author

EitNickS commented Dec 12, 2022 via email

@EitNickS
Copy link
Author

@bastibe Looking at your posted .pngs a little further, the quartiles have always been diagonal. (the second graph's shadows outer edges have the diagonal lines seen in mine). Does this mean that those graphs also had an indexing issue? Or is this a stylistic approach. If the former, why are angles varying depending on the individual violin (some may have a 5 degree angle, while others are almost at a 0 degree).

@EitNickS
Copy link
Author

I found out specifically that creating two y axes is causing the issue, but oddly enough, doing this for functions such as boxPlotGroup (or boxplots in general) does not cause an issue. See attached pngs for comparison.

Now why this is doing that, or how to fix it, is not something that I have figured out, but I do know that leaving them with y axis is really not a great way to depict the data, as you can't see significant detail like you can in the zoomed in right half violins using their own axis.

Screen Shot 2022-12-12 at 2 50 08 PM

Screen Shot 2022-12-12 at 2 49 43 PM

Screen Shot 2022-12-12 at 2 50 47 PM

@bastibe
Copy link
Owner

bastibe commented Dec 13, 2022

I found out specifically that creating two y axes is causing the issue, but oddly enough, doing this for functions such as boxPlotGroup (or boxplots in general) does not cause an issue. See attached pngs for comparison.

That is very strange indeed. Perhaps we should just set(obj.ViolinPlot, "Marker", "none") and set(obj.ViolinPlot2, "Marker", "none") at the very end of the Violin constructor, just to be safe.

Looking at your posted .pngs a little further, the quartiles have always been diagonal.

Oh that's a shame! Thank you for finding the bug, though!

The relevant code for plotting the quartiles is probably in Violin.m:291:

            %% Plot the quartiles within the violin
            quartiles = quantile(data, [0.25, 0.5, 0.75]);
            flat= [halfViol*pos halfViol*pos];
            switch args.QuartileStyle
                case 'shadow'
                    switch args.HalfViolin
                        case 'right'
                            w = [pos+density*width halfViol*pos];
                            h= [value value(end:-1:1)];
                        case 'left'
                            w = [halfViol*pos pos-density(end:-1:1)*width];
                            h= [value value(end:-1:1)];
                        case 'full'
                            w = [pos+density*width pos-density(end:-1:1)*width];
                            h= [value value(end:-1:1)];
                    end
                    w(h<quartiles(1))=flat(h<quartiles(1));
                    w(h>quartiles(3))=flat((h>quartiles(3)));
                    obj.ViolinPlotQ =  ... % plot color will be overwritten later
                        fill(w, ...
                        h, [1 1 1]);
                case 'boxplot'
                    obj.BoxPlot = ... % plot color will be overwritten later
                        fill(pos+[-1,1,1,-1]*args.BoxWidth, ...
                        [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ...
                        [1 1 1]);
                case 'none'
            end

Since it is affecting only the left/right case, but not the full violin, I would assume that the error is related to the variable only used in the left/right case, i.e. halfViol. Probably, halfViol is either one element too long or one element too short. You could probably debug this by comparing the length of halfViol and density at this point in the code.

Or is this a stylistic approach.

No, the quartiles were always intended to be perfectly horizontal. The fact that they are not is definitely a bug in my book.

@EitNickS
Copy link
Author

Hey @bastibe, I think we may be talking about different aspects of the graphs here, because after looking at the code, and comparing it to your posted pngs (attached below), and a full violin construction of my own code, it appears that the shadowing always has been diagonal, for even the full violin. See the arrows for the exteriors of the quartile shadowing, and their respective diagonal lines for the png that you have posted for the code.

Screen Shot 2022-12-14 at 12 43 45 PM

Here is a full violin using my own code, with differing degrees of angles for the shadow for different lines violins.

Screen Shot 2022-12-14 at 2 59 28 PM

and here a screenshot snippet of the code I used to create this:

Screen Shot 2022-12-14 at 2 59 42 PM

If you can point me to what you think may be happening with this new piece of information, I can look at potentially pull requesting it with modified code, as it no longer pertains to just half violins. Thank you for your help in trying to make your creation even better!!

@bastibe
Copy link
Owner

bastibe commented Dec 15, 2022

Thank you for that analysis. This indeed put it into perspective, and helped me figuring it out. I think I understand what's happening now:

Again, referencing Violin.m:291:

            %% Plot the quartiles within the violin
            quartiles = quantile(data, [0.25, 0.5, 0.75]);
            flat= [halfViol*pos halfViol*pos];
            switch args.QuartileStyle
                case 'shadow'
                    switch args.HalfViolin
                        case 'right'
                            w = [pos+density*width halfViol*pos];
                            h= [value value(end:-1:1)];
                        case 'left'
                            w = [halfViol*pos pos-density(end:-1:1)*width];
                            h= [value value(end:-1:1)];
                        case 'full'
                            w = [pos+density*width pos-density(end:-1:1)*width];
                            h= [value value(end:-1:1)];
                    end
                    w(h<quartiles(1))=flat(h<quartiles(1));                            %
                    w(h>quartiles(3))=flat((h>quartiles(3)));                          %
                    obj.ViolinPlotQ =  ... % plot color will be overwritten later      % ③
                        fill(w, ...
                        h, [1 1 1]);
                case 'boxplot'
                    obj.BoxPlot = ... % plot color will be overwritten later
                        fill(pos+[-1,1,1,-1]*args.BoxWidth, ...
                        [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ...
                        [1 1 1]);
                case 'none'
            end

In ③, the violin is plotted as a fill plot between x and y coordinates, called w and h respectively (presumably "width" and "height"). h just goes up, then down, in a regular grid. w contains first the left half of the violin (optionally pos, i.e. the centerline), then the right half (again optionally centerline). The way the quartiles are implemented is by setting all w above and below the quartile to centerline, in ① and ②.

However, this means that w steps from the violin outline to the centerline over the height of one h step, which forms a diagonal line. I think the solution to this conundrum is to append after ②:

h(h<quartiles(1)) = quartiles(1);
h(h>quartiles(3)) = quartiles(3);

Thus bunching up all the high/low quartile points at the centerline between the quartiles. A better solution would actually just cut out the high/low quartile points, but that's more complicated and probably not worth the effort.

@EitNickS
Copy link
Author

EitNickS commented Jan 4, 2023

I amended Violin.m as shown below:
Screen Shot 2023-01-04 at 12 31 59 PM

But when calling 'Violin' explicitly (not violinplot), it still has slanted shadows.

I think for the time being, I will just use the quartiles: 'boxplot' instead, at least until I can sort this out, or there are other suggestions.

Thanks for your help, despite it not being fixed. I appreciate it all!

Nick

@bastibe
Copy link
Owner

bastibe commented Jan 5, 2023

For debugging purposes, could you print out a set of w and h after these modifications?

@mragostini
Copy link

Hi,
I am trying to plot the quartile shadow area and got the same strange behavior.

The possible error lies in the fact that the "density" vector has only 100 values then possibly further reduced. This is because the MATLAB function "ksdensity" assigns 100 values to the density vector if the desired length is not specified.
Having few values, the fill plot function is forced to interpolate between few values. As an example, two adjacent values of the "density" vector are shown in the image.

image

To solve this problem, I first calculated the density function with 100 points so that I had the minimum and maximum value of the vector "values". Then I repeated the calculation specifying that I wanted to get the density function for 1000 points (a totally arbitrary value) between the aforementioned min and max.
This allows a much more fine definition of the density area and almost eliminate the tilted quartile line.

image

image

Unfortunately his is not a final solution, just a quick workaround since the quartile line is not perfectly horizontal.
Hope it helped!

@bastibe
Copy link
Owner

bastibe commented Mar 10, 2023

This definitely helps, thank you very much! Would you like to contribute your change as a pull request?

NicolasClairis added a commit to NicolasClairis/Violinplot-Matlab that referenced this issue Mar 20, 2024
fix for the problem appearing when one uses yyaxis left or yyaxis right where circles appear all around the violin based on the solution proposed here bastibe#59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants