I want to draw circles with semitransparent colour on top of a map (showing prevalence), the colour at overlap areas is too heavy to show the background image, the code am using is too long to put here, but for circles am using:
symbols(data[,c(9, 10)],
circles = 5/(pi * a.rad * cos(atan(b.rad / a.rad * tan((data[,10])*pi/180)))/180000),
fg = NULL,
bg = rgb(0, 1, 0, 0.18),
inches = F,
add = T)
I want to joint circles to plot them with the same "transparent" colour, is there some way to do this using R? Or is there any other alternative using R*?
*Please note: The maps am using are develped using R, and I can't use other program to develop them.
There's no simple way, since you're plotting a series of circles. If you don't demand the fill color be consistent, you could set the alpha value to a very small level. To get a consistent fill color, you'll have to calculate the intersection areas of the circles and plot those areas as well as the nonintersection areas (instead of plotting circles). If you have multiple overlapping circles, you can see that will become a computational nightmare.
My personal recommendation is to plot with the smallest visible alpha value so that the worst-case overlap area doesn't obscure the map. This has the side-effect of clearly indicating the density of overlap areas.
Related
I am plotting some geometry using bokeh and came across this. I am plotting a rectangle with equal sides (i.e. a square), and in that square, plotting a circle with diameter = width of the square. The circle should tangent to the square at edges, but it is not.
here is the code:
from bokeh.plotting import output_notebook, figure, show
output_notebook()
p = figure(width=500, height=500)
p.rect(0, 0, 300, 300, line_color='black')
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
p.axis.minor_tick_out = 0
show(p)
Which results in this:
Is there anything I am doing wrong or could change to make the circle fit exactly in the square?
Thanks in advance,
Randall
Here's another case - just drawing a circle:
p = figure(width=500, height=500, x_range=(-150, 150), y_range=(-150, 150))
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
show(p)
radius of the circle is 150 in the x direction, but not the y-direction.
I would like to report that as of Bokeh 0.12.7, this issue can now be fixed in a simpler manner.
As described in other posts, the main issue is not that the circle is not a circle, but that the square is not a square. This is due to the fact that actual area on which Bokeh draws the figure (the canvas) is usually not a square by default or even when the width and height are set to the same value. Bokeh by default will attempt to draw a figure by using up all the space on the canvas. This creates a mismatch between the data distance and the pixel distance of the plot.
As of 0.12.7, figures can now accept a match_aspect property which when set to True will will match the aspect of the data space to the pixel space of the plot.
In your example, simply adding the match_aspect = True in your figure
p = figure(width=500, height=500, match_aspect=True,
title="Circle touches all 4 sides of square")
p.rect(0, 0, 300, 300, line_color='black')
p.circle(x=0, y=0, radius=150, line_color='black',
fill_color='grey', radius_units='data')
will now produce
UPDATE: Please note new answer by #DuCorey below. As of Bokeh 0.12.7, aspect control is now available, for situations like this.
The issue is actually that the square is not square, and that is because the pixel aspect ratio and the "data" aspect ratio do not match. i.e., the distance per pixel is different in the x direction than it is in the y direction.
There are a few options:
You can use various properties to control the dimensions of the central plot area (e.g. plot border width and axis tick label orientation) You can also control you data ranges explicitly. In other words, you can make the aspect ratios match, and then the circle and rect will match
You can use absolute pixel units (e.g. size for a circle, and use a large square marker instead of rect) instead of "data" units.
Alternatively, if you want a circle that "deforms" when the aspects do not match, then your best bet is to use an ellipse with an identical width and height, which will work because in this case bokeh has two dimensions to use to measure (instead of the single radius) and can match each to the scale along each dimension independently.
(This is actually the fundamental difference that explains the behaviour: rect has two dimensions to measure independently. circle does not, it only has one, and has to arbitrarily use the x or y dimension to measure distance per pixel)
ok, based on the suggestions, I tried a few things.
Changed the orientation of the y-axis tick labels - still
had issue.
Changed various stand-offs, even moving in the tick
labels inside the plot (with a negative offset). Did not work either.
Changed the x_range and r_range in figure() to be equal tuples. Did not work either
Changes the plot_height (decreased it), and I could eventually, through rial and error, get the circle to fit in the square with a plot_height that was < plot width.
Lots of great practice controlling attributes of the plot. Time will invested.
However, the last change I tried worked the best. It was one of the first suggestions - change the plot border.
Weirdly, setting p.min_border=40, which on 0.12.6 is the default value, and voila, it appears the chart aspect ratio for a chart where plot_width=plot_height is truly 1 on the screen as well.
p = figure(plot_width=500, plot_height=500)
p.rect(0, 0, 300, 300, line_color=None)
p.circle(x=0, y=0, radius=150, line_color=None,
fill_color='lightgrey', radius_units='data')
p.min_border=40
show(p)
Before and after images showing the effect of adding p.min_border=40. Any value over ~33 appeared to be enough force the plot area to have the same screen x and y dimension - so the square was really a square (and the circle fit inside).
The reason for this is that you're creating a circular marker (or circle glyphs) and placing it at position (0, 0), while it seems like you want to create a circle centered at 0.
I think the rect here "happens" to work because it can scale correctly in both dimensions and remain a "rectangle".
Keyword Args:
radius (UnitsSpecPropertyDescriptor) : The radius values for circle markers (in "data space" units, by default). (default None)
radius_dimension (BasicPropertyDescriptor) : What dimension to measure circle radii along. (default 'x')
radius_units (Enum('screen', 'data')) : (default 'data')
I guess my point is here you've taken a shortcut by trying to use a "glyph" as your plot and specifying the units to be the data units.
If you want to create an actual circle you could do the following:
th = np.linspace(0, 2*np.pi)
r = 150
p = figure(width=500, height=500)
p.rect(0, 0, 300, 300, line_color='black')
p.line(r * np.cos(th), r * np.sin(th), line_color='black')
# p.circle(x=0, y=0, radius=150, line_color='black',
# fill_color='grey', radius_units='data')
p.axis.minor_tick_out = 0
show(p)
Notice the above is harder to fill (I didn't bother) because I'm guessing you need to define some closed polygon function while I only defined a line that happens to be a closed polygon, in this case a circle.
Not sure, but the bleu rectangle is not your rectangle.
Replace:
p.rect(0, 0, 300, 300, line_color='black')
By:
p.rect(-150, -150, 150, 150, line_color='black')
I have human facial data as below:
library(Rvcg)
library(rgl)
data(humface)
lm <- matrix(c(1.0456182e+001, -3.5877686e+001, 5.0972912e+001, 2.2514189e+001,
8.4171227e+001, 6.6850304e+001, 8.3239525e+001, 9.8277359e+000,
6.5489395e+001, 4.2590347e+001, 4.0016006e+001, 5.9176712e+001),
4)
shade3d(humface, col="#add9ec", specular = "#202020", alpha = 0.8)
plot3d(lm, type = "s", col = "red", xlab = "x", ylab = "y", zlab = "z",
size = 1, aspect = FALSE,add=T)
for lm, four landmarks are placed on the surface of the mesh, in the following oder:
The yellow lines are drawn by hand for illustration purpose. I wish to calculate the surface area of the quarilateral enclosed by the four red dots, i.e., the surface area inside the yellow edges.
If surface area cannot be calculated, I also welcome methods to calculate the area (not area of the surface of the face) of the quadrilateral. I know one could calculate the sum of areas of triangle 123 and triangle 234. However, I my real application, I have no idea of the ordering and relative spatial position of the four points. Since I have thousands of qudrilateral areas to calculate, it is impossible to plot each quadrilateral and determine how to decompose the quadrilateral into two triangles. For example, I may accidentally pick triangle 123 and triangle 124, and the sum of these two triangle ares is not what I want.
Therefore, I am interested in either surface area or area of the quadrilateral. Solution to either is welcome. I just do not want to plot each quadrilateral and I want an area value directly computed from the coordinates.
The rgl::shadow3d function can compute a projection of the quad onto the face. Then you'd compute the area by summing the areas of triangles and quads in the result. #DiegoQueiroz gives you some pointers for doing that,
plus the Rvcg package contains vcgArea:
quad <- mesh3d(lm, triangles = cbind(c(1,2,4), c(1,4,3)))
projection <- shadow3d(humface, quad, plot = FALSE)
Here's what that looks like:
shade3d(projection, col = "yellow", polygon_offset = -1)
The projection ends up containing 3604 triangles; the area is
vcgArea(projection)
# [1] 5141.33
There are a few ambiguities in the problem: the quadrilateral isn't planar, so you'd get a different one if you split it into triangles along the other diagonal. And the projection of the quad onto the face is different depending on which direction you choose. I used the default of projecting along the z axis, but in fact the face isn't perfectly aligned that way.
EDITED TO ADD:
If you don't know how to decompose the 4 points into a single quadrilateral, then project all 4 triangles (which form a tetrahedron in 3-space):
triangles <- mesh3d(lm, triangles = cbind(c(1,2,3), c(1,2,4), c(1,3,4), c(2,3,4))
projection <- shadow3d(humface, triangles, plot = FALSE)
This gives a slightly different region than projecting the quad:
vcgArea(projection)
# [1] 5217.224
I think the reason for this is related to what I referred to in the comment above: the area depends on the "thickness" of the object being projected, since the quad is not planar.
I believe your question is more appropriate for math.stackexchange.com because I think it's more a question about the math behind the code than the code itself.
If you are concerned about precision, you may want to use techniques for smoothing the calculated area of a mesh, like the one presented in this paper.
However, if you don't really need that area to really model the surface, then you can ignore the face and compute the convex quadrilateral area using the many available formulas for that, however, the simplest one requires you to have the vectors that correspond to the quadrilateral's diagonals (which you can find by checking this question)
If you decide to find the diagonals and use the simplest vectorial formula (half the magnitude of the cross-product between the diagonals), you should use the cross() and Norm() functions from the pracma package as R's crossprod() computes a different type of cross product than the one you will need.
I've been trying to figure out how to make the little circles that represent mutation steps on a haplotype network bigger. For whatever reason, all the normal ways I'd think don't seem to be working. It seems like no matter what I do, the symbols remain tiny. What am I missing?
Here's a base bit of sample code:
data(woodmouse)
h <- haplotype(woodmouse)
net <- haploNet(h)
plot(net, size=attr(net,"freq")*3,bg=pal,labels=F, fast=F, legend=F,
show.mutation=T,threshold=0)
# using scale.ratio = 1, the mutations are visisble
plot(net, size=attr(net,"freq")*3,bg=pal,labels=F, fast=F, legend=F,
show.mutation=T,threshold=0,scale.ratio=3)
# but using scale.ratio=3, they get tiny / disappear
You can see the mutations here, but if I set scale.ratio to something bigger (a requirement with my own data), they essentially disappear.
I've tried passing a larger cex to plot (doesn't work) as well as setting cex globally with par (makes the whole plot smaller for some reason).
It seems like the circles are scaled with the lines, but I don't know how to control that. Is it even possible? Am I missing something really obvious?
cex controls the font size and will not help with graphics size. From the help page for the haploNet and plot.haploNet functions:
?haploNet
size: a numeric vector giving the diameter of the circles representing the haplotypes: this is in the same unit than the links and eventually recycled.
scale.ratio: the ratio of the scale of the links representing the number of steps on the scale of the circles representing the haplotypes. It may be needed to give a value greater than one to avoid overlapping circles.
This means that size of the links (circles representing mutations between haplotypes) is relative to the size of the haplotypes. To relatively enlarge the link size, you need to find a suitable combination of the two arguments.
set.seed(123)
net <- haploNet(haplotype(woodmouse[sample(15, size = 50, replace = TRUE), ]))
par(mfrow=c(1,2))
plot(net, size=attr(net,"freq"), labels=F, fast=F, legend=F,
show.mutation=T, threshold=0, scale.ratio=1)
plot(net, size=attr(net,"freq")*.2, labels=F, fast=F, legend=F,
show.mutation=T, threshold=0, scale.ratio=.2)
Enlarging circle size with haploNet runs into a risk that circles will overlap and the visible number of mutations will be incorrect. Use discretion with visualization and in case of problems, consider haplotype network calculation in TCS software where unsampled mutations are displayed with vertical bars or Network from Fluxus with proportional link lengths.
I am attempting to create a block of color in R based on density values. So for example given 10 values with a density distribution:
values=c(0,1,2,3,4,5,6,7,8,9)
densities=c(0.7, 0.1, 0.05,0,0,0,0,0.05,0.05,0.05)
I want to create what is essentially a colored bar where the greatest density is e.g. black and the least white, with the intermittent values somewhere in between but proportionally so i.e. 0.05 if half as dark as 0.1 AND similar values are the same color.
As I think of it I can just create a bar chart, with all bars the same height, no borders etc and the densities used to create the colors. However, no matter how hard I try I can't figure out how to get the color scheme correct.
I have created a gradient but this isn't linked to density. Also I have linked color to density with densCols but I haven't managed to make the colors sequential.
Can someone point me in the right direction? I have seen similar questions but none that have gotten me to the point I need to be at. I would prefer to do this with the base graphics package if possible.
Thanks in advance.
if you can normalize value in [0,1] range you can use functions like grey (grey scale).
## not with density but with probability example
val<- rnorm(100) < 0.5
mean(val)
grey(0) # black
grey(1) # white
## color intensity proportional to probability
grey(1 - prop.table(table(val)))
I would like to produce what I think will be a very simple diagram in R - it will show the number of genes that fall in to one of two categories.
The area of circles must be relative to each other and show the vast difference between the number of counts in my two categories. One category is 15000 the other is 15. Therefore the area of one circle should be 1000 times greater than the other. Is there a simple R script that can be used to do this? (Draw two circles, one of which there area is X times smaller than the other)
You can draw circles using the plotrix package and draw.circle function. So to answer your question, we just need to calculate the radius of each circle. To make the comparison, it's easier to make the first circle have unit area. So,
## Calculate radius for given area
get_radius = function(area = 1) sqrt(area/pi)
##Load package and draw blank graph
library(plotrix)
plot(-10:10,seq(-10,10,length=21),type="n",xlab="",ylab="")
## Unit area
draw.circle(0, 0, get_radius())
## 10 times larger
draw.circle(0, 0, get_radius(10))
As shown in this post, you can use for example the shape package and use the function plotcircle where you can chose the radius. Example:
require("shape")
emptyplot(c(0, 1))
plotcircle(mid = c(0.2, 0.5), r = 0.1)
plotcircle(mid = c(0.6, 0.5), r = 0.01)