I am trying to make a swam plot that contains more information than a single categorical level and two variables. I am looking to create something like this
So ideally, something like this would work (but it does not):
ax = sns.swarmplot(x="round_id", y="independent_error_abs", hue="difficulty", hue_order=['easy','medium','hard'], size="followers", markershape="rank",data=df)
where "difficulty", "followers", and "rank" determine the color of the point, the size of the point, and the shape of the point, respectively.
No, this is not possible with swarmplot. Personally I find this kind of plot very difficult to interpret: a good statistical plot should make the patterns in the data immediately apparent, whereas plots with multiple categorical variables that manipulate the size or shape of the points quickly become more like puzzles. My recommendation in these cases (following Andrew Gelman) is to make more than one plot, each with relatively simple semantics.
You don't have to agree, of course, but you will have to make it yourself using matplotlib.
I am facing the same issue, and actually the solution seems to be pretty simple at least for the marker type!
Just divide your dataframe in subdataframes, each for a different marker type. The you make a swarmplot on top of each other, and that's it.
If the size of the dot, is also a categorical variable, you just need to do the same as above where each subdtaframe will represent a marker and a different size.
If size is continuous, then it seems you would need to plot each dot independently in a for loop, but for that I would use matplotlib.pyplot.
Related
I want to plot a PDF function given data which follows a normal distribution. Mainly I followed this link.
Now, if I am working on the data created like on that website (x=np.linspace()) and I plot it with either seaborn.lineplot() or matplotlib.pyplot.plot(), I get a normal curve as shown on the website linked above. But when I do this with my own data (which I believe is normal, but with a lot more data points) instead of initializing it with np.linspace I get a clear normal curve with seaborn's lineplot and a messy normal curve with matplotlib's plot function.
I have tried to look for default arguments on both functions but couldn't find any (except estimator) which would cause this behavior. The estimator argument of Seaborn's lineplot was the only argument that looked like it could do something like this but setting it to None did not make any difference (and it kind of makes sense I think since the y value is always same for a specific x so averaging out will produce the same value).
I used to think both functions are the same, but then why do they have different output?
The Seaborn lineplot function has the default parameter sort=True.
So unless you tell it not to, it'll order the data for you. This is not something which pyplot.plot() does, instead it'll draw lines between the points in the order provided.
If you want to order the data before plotting it using Pyplot, there's a good solution for how to do that.
I am trying to create a type of map that plots a route between points.
As such I have something that looks like the image below:
And as you can see, the bll label is very close to the data point. I would like it to be a bit further away, so you can actually see the dot.
Also, the text is just a regular ax.text plot with x and y values.
My problem is, that yes, I could just add some kind of percentage value or something. However, depending on the coordinate and x-value, this will not be the same depending on you being very far left, where x => 0, or far right where x => max value of the plot. Then you could argue I could just add 10 or 20 units, but in my case I produce different maps depending on different routes. So this means that the x-axis values are not the same. Sometimes the map is big, and sometimes it's small. So using the same value in all maps will make bll text move either very much or very little depending on size.
Also, if I am ever to zoom on the map, this would adjust the text as well if I were to use some kind of value extension, since the distance between the data point AND the text will also increase relative to the size of the zoom, like this figure:
Can this be solved in a somewhat simple manner ?
Best regards
I have come across a number of plots (end of page) that are very similar to scatter / swarm plots which jitter the y-axis in order avoid overlapping dots / bubbles.
How can I get the y values (ideally in an array) based on a given set of x and z values (dot sizes)?
I found the python circlify library but it's not quite what I am looking for.
Example of what I am trying to create
EDIT: For this project I need to be able to output the x, y and z values so that they can be plotted in the user's tool of choice. Therefore I am more interested in solutions that generate the y-coords rather than the actual plot.
Answer:
What you describe in your text is known as a swarm plot (or beeswarm plot) and there are python implementations of these (esp see seaborn), but also, eg, in R. That is, these plots allow adjustment of the y-position of each data point so they don't overlap, but otherwise are closely packed.
Seaborn swarm plot:
Discussion:
But the plots that you show aren't standard swarm plots (which almost always have the weird looking "arms"), but instead seem to be driven by some type of physics engine which allows for motion along x as well as y, which produces the well packed structures you see in the plots (eg, like a water drop on a spiders web).
That is, in the plot above, by imagining moving points only along the vertical axis so that it packs better, you can see that, for the most part, you can't really do it. (Honestly, maybe the data shown could be packed a bit better, but not dramatically so -- eg, the first arm from the left couldn't be improved, and if any of them could, it's only by moving one or two points inward). Instead, to get the plot like you show, you'll need some motion in x, like would be given by some type of physics engine, which hopefully is holding x close to its original value, but also allows for some variation. But that's a trade-off that needs to be decided on a data level, not a programming level.
For example, here's a plotting library, RAWGraphs, which produces a compact beeswarm plot like the Politico graphs in the question:
But critically, they give the warning:
"It’s important to keep in mind that a Beeswarm plot uses forces to avoid collision between the single elements of the visual model. While this helps to see all the circles in the visualization, it also creates some cases where circles are not placed in the exact position they should be on the linear scale of the X Axis."
Or, similarly, in notes from this this D3 package: "Other implementations use force layout, but the force layout simulation naturally tries to reach its equilibrium by pushing data points along both axes, which can be disruptive to the ordering of the data." And here's a nice demo based on D3 force layout where sliders adjust the relative forces pulling the points to their correct values.
Therefore, this plot is a compromise between a swarm plot and a violin plot (which shows a smoothed average for the distribution envelope), but both of those plots give an honest representation of the data, and in these plots, these closely packed plots representation comes at a cost of a misrepresentation of the x-position of the individual data points. Their advantage seems to be that you can color and click on the individual points (where, if you wanted you could give the actual x-data, although that's not done in the linked plots).
Seaborn violin plot:
Personally, I'm really hesitant to misrepresent the data in some unknown way (that's the outcome of a physics engine calculation but not obvious to the reader). Maybe a better compromise would be a violin filled with non-circular patches, or something like a Raincloud plot.
I created an Observable notebook to calculate the y values of a beeswarm plot with variable-sized circles. The image below gives an example of the results.
If you need to use the JavaScript code in a script, it should be straightforward to copy and paste the code for the AccurateBeeswarm class.
The algorithm simply places the points one by one, as close as possible to the x=0 line while avoiding overlaps. There are also options to add a little randomness to improve the appearance. x values are never altered; this is the one big advantage of this approach over force-directed algorithms such as the one used by RAWGraphs.
I am using Python's matplotlib.pyplot.contourf to create a contour plot of my data with a color bar. I have done this successfully countless times, even with other layers of the same variable. However, when the values get small (on the order of 1E-12), parts of the contour show up white. The white color does not show up in the color bar either. Does anyone know what causes this and how to fix this? The faulty contour is attached below.
a1 = plt.contourf(np.linspace(1,24,24),np.linspace(1,20,20),np.transpose(data[:,:,15]))
plt.colorbar(a1)
plt.show()
tl;dr
Given the new information, matplotlib couldn't set the right number of levels (see parameters in the documentation) for your data leaving data unplotted. To fix that you need to tell matplotlib to extend the limits with either plt.contourf(..., extend="max") or plt.contourf(..., extend="both")
Extensive answer
There are a few reasons why contourf() is showing white zones with a colormap that doesn't include white.
NaN values
NaN values are never plotted.
Masked data
If you mask data before plotting, it won't appear in the plot. But you should know if you masked your data.
Although, you may have unnoticed mask your data if you use something like Tick locator = LogLocator().
Matplotlib couldn't set the right levels for your data
Sometimes matplotlib doesn't set the right levels, leaving some of your data without plotting.
To fix that you can user plt.contourf(..., extend=EXTENDS) where EXTENDS can be "neither", "both", "min", "max"
Coarse grid
contourf plots whitespace over finite data. Past answers do not correct
One remark, white section in the plot can also occur if the X and Y vectors data points are not equally spaced. In that case best to use function tricontourf().
I was facing the same problem recently, when there was data available even higher/lower than the levels I have set. So, the plt.contourf fills the contours exclusively given by you, and it neglects any other higher or lower values present in your data.
I solved this by adding a key word argument extend="both", which for your case would be something like this:
a1 = plt.contourf(np.linspace(1,24,24),np.linspace(1,20,20),np.transpose(data[:,:,15]), extend="both")
or in general form:
a1 = plt.contourf(x,y,variable[:,:,15],extend="both")
By doing this, you're instructing the module to plot the higher(/lower) values according to the highest(/lowest) filled contour.
If you want only to extend in the lower or higher range, you can change the keyword argument to
extend="min" or extend ="max"
I have one question for plotly x and y axes setting.
It's possible to autorange the axis only the first time based on all input data and then turn off rescaling while manipulating the data input (in the legend)?
https://plot.ly/python/reference/#layout-xaxis-type
From documentation: 'autorange' default: True
Determines whether or not the range of this axis is computed in relation to the input data.
I need autorange only to be done first time and then it should behave like False. I need it so I can make evaluations relative to the whole dataset.
Maybe it can be done another way, not by manipulating autorange but that's why I'm asking.
MY EXAMPLE:
Imagine you have visualization like this. I have labeled many groups so that I can turn them off/on by plotly functionality. But the problem for me is that it is rescaling everytime based on the input data (only the ones which are 'turned on').
This is after I isolate the GROUP 1. But I want the same x-axis and y-axis as I had before (which was 'autoranged' when I started the visualization).
Thanks for your help!