First of all, please download my data set from http://alexandervanloon.nl/survey_oss.csv and then execute the following content of a script to get a few scatter plots:
# read data and attach it
survey <- read.table("survey_oss.csv", header=TRUE)
attach(survey)
# plot for inhabitants
png("scatterINHABT.png")
plot(INHABT, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS", las=1)
abline(lm(OSSADP~INHABT)) # regression line (y~x)
dev.off()
# plot for inhabitants divided by 1000
png("scatterINHABT_divided.png")
plot(INHABT/1000, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS", las=1)
abline(lm(OSSADP~INHABT)) # regression line (y~x)
dev.off()
# plot for inhabitants in logarithmic scale
png("scatterINHABT_log.png")
plot(INHABT, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS", las=1, log="x")
abline(lm(OSSADP~INHABT)) # regression line (y~x)
dev.off()
# plot for inhabitants in logarithmic scale and divided by 1000
png("scatterINHABT_log_divided.png")
plot(INHABT/1000, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS", las=1, log="x")
abline(lm(OSSADP~INHABT)) # regression line (y~x)
dev.off()
As you can see, in the first scatterplot the problem is that R decides to use scientific notation and the data looks odd because of outliers. That's why I'd like to have the inhabitants on x-axis in thousands and have the x-axis use a logarithmic scale as well.
The problem is twofold. First, I can get rid of scientific notation by simply dividing the inhabitants by 1000, but this produces a flat horizontal regression line unlike the first plot. I know there are other ways to fix this such as Do not want scientific notation on plot axis but I couldn't adapt the code there to my situation.
Second, switching the x-axis to a logarithmic scale also makes the regression line flat. Google points to https://stat.ethz.ch/pipermail/r-help/2006-January/086500.html as a first result for a possible solution and I tried using abline(lm(OSSADP~log10(INHABT))) which is suggested there, but that produces a vertical regression line. And if I divide both by 1000 and use a logarithmic scale, the line is also horizontal.
I'm a social scientist without any background in mathematics and statistics, so I fear I might have missed something obvious, if so my apologies. Thank you all very much for any potential help.
The scientific notation was covered on the R mailing list a while ago, but you can control how R chooses when to go to scientific notation with options()$scipen
.
options(scipen=10)
plot(INHABT, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS")
Second, the problem with your dividing by 1000 is that you didn't divide by a thousand in both the plot and the abline. This would do the trick:
plot(INHABT/1000, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS")
abline(lm(OSSADP~I(INHABT/1000))) # Fixed regression line.
The I is neccessary because the / symbol has a different meaning in formulas.
Also, your las parameter is unnecessary.
I solved the problem of horizontal line when use log="x" like this:
plot(INHABT, OSSADP, xlab="Inhabitants", ylab="Adoption of OSS", log="x")
abline(lm(OSSADP~log10(INHABT)))
with log10 and not just log.
Related
I am currently working with a line plot in R that would contain a large number of separate plots (which does make it difficult to read). What I would like to do instead is to somehow create a light-colored shade that captures the range of individual line plots. Would that be possible? This is what I have for my plot:
plot(get,Hope2, type="l",col="red", lwd="3", xlab="Cumulative CO2
emissions (TtC)", ylab="One-day maximum precipitation (mm/day)",
main="One-day maximum precipitation for South Sudan for CanESM2
scenarios")
lines(get2.teratons, Hope3, type="l", lwd="3", col="red")
lines(get3.teratons, Hope4, type="l", lwd="3", col="red")
lines(get4.teratons, Hope5, type="l", lwd="3", col="red")
So, this gives 4 separate red lines on the same plot (I am likely going to place up to 10 lines, so you can imagine how cluttered that would look without shading them in the background!). Now, let's say that I wanted to create a light red shade that fills from the upper to lower red curves. How should I go about doing that? The idea would be to capture all of the line plots by doing something like this:
http://jvoigts.scripts.mit.edu/blog/assets/plot_shaded_pretty.png
Thanks, and I would greatly appreciate any help!
Scatter plots are useless when number of plots is large.
So, e.g., using normal approximation, we can get the contour plot.
My question: Is there any package to implement the contour plot from scatter plot.
Thank you #G5W !! I can do it !!
You don't offer any data, so I will respond with some artificial data,
constructed at the bottom of the post. You also don't say how much data
you have although you say it is a large number of points. I am illustrating
with 20000 points.
You used the group number as the plotting character to indicate the group.
I find that hard to read. But just plotting the points doesn't show the
groups well. Coloring each group a different color is a start, but does
not look very good.
plot(x,y, pch=20, col=rainbow(3)[group])
Two tricks that can make a lot of points more understandable are:
1. Make the points transparent. The dense places will appear darker. AND
2. Reduce the point size.
plot(x,y, pch=20, col=rainbow(3, alpha=0.1)[group], cex=0.8)
That looks somewhat better, but did not address your actual request.
Your sample picture seems to show confidence ellipses. You can get
those using the function dataEllipse from the car package.
library(car)
plot(x,y, pch=20, col=rainbow(3, alpha=0.1)[group], cex=0.8)
dataEllipse(x,y,factor(group), levels=c(0.70,0.85,0.95),
plot.points=FALSE, col=rainbow(3), group.labels=NA, center.pch=FALSE)
But if there are really a lot of points, the points can still overlap
so much that they are just confusing. You can also use dataEllipse
to create what is basically a 2D density plot without showing the points
at all. Just plot several ellipses of different sizes over each other filling
them with transparent colors. The center of the distribution will appear darker.
This can give an idea of the distribution for a very large number of points.
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=c(seq(0.15,0.95,0.2), 0.995),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.15, lty=1, lwd=1)
You can get a more continuous look by plotting more ellipses and leaving out the border lines.
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=seq(0.11,0.99,0.02),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.05, lty=0)
Please try different combinations of these to get a nice picture of your data.
Additional response to comment: Adding labels
Perhaps the most natural place to add group labels is the centers of the
ellipses. You can get that by simply computing the centroids of the points in each group. So for example,
plot(x,y,pch=NA)
dataEllipse(x,y,factor(group), levels=c(seq(0.15,0.95,0.2), 0.995),
plot.points=FALSE, col=rainbow(3), group.labels=NA,
center.pch=FALSE, fill=TRUE, fill.alpha=0.15, lty=1, lwd=1)
## Now add labels
for(i in unique(group)) {
text(mean(x[group==i]), mean(y[group==i]), labels=i)
}
Note that I just used the number as the group label, but if you have a more elaborate name, you can change labels=i to something like
labels=GroupNames[i].
Data
x = c(rnorm(2000,0,1), rnorm(7000,1,1), rnorm(11000,5,1))
twist = c(rep(0,2000),rep(-0.5,7000), rep(0.4,11000))
y = c(rnorm(2000,0,1), rnorm(7000,5,1), rnorm(11000,6,1)) + twist*x
group = c(rep(1,2000), rep(2,7000), rep(3,11000))
You can use hexbin::hexbin() to show very large datasets.
#G5W gave a nice dataset:
x = c(rnorm(2000,0,1), rnorm(7000,1,1), rnorm(11000,5,1))
twist = c(rep(0,2000),rep(-0.5,7000), rep(0.4,11000))
y = c(rnorm(2000,0,1), rnorm(7000,5,1), rnorm(11000,6,1)) + twist*x
group = c(rep(1,2000), rep(2,7000), rep(3,11000))
If you don't know the group information, then the ellipses are inappropriate; this is what I'd suggest:
library(hexbin)
plot(hexbin(x,y))
which produces
If you really want contours, you'll need a density estimate to plot. The MASS::kde2d() function can produce one; see the examples in its help page for plotting a contour based on the result. This is what it gives for this dataset:
library(MASS)
contour(kde2d(x,y))
I have to plot a few different simple linear models on a chart, the main point being to comment on them. I have no data for the models. I can't get R to create a plot with appropriate axes, i.e. I can't get the range of the axes correct. I think I'd like my y-axis to 0-400 and x to be 0-50.
Models are:
$$
\widehat y=108+0.20x_1
$$$$
\widehat y=101+2.15x_1
$$$$
\widehat y=132+0.20x_1
$$$$
\widehat y=119+8.15x_1
$$
I know I could possibly do this much more easily in a different software or create a dataset from the model and estimate and plot the model from that but I'd love to know if there is a better way in R.
As #Glen_b noticed, type = "n" in plot produces a plot with nothing on it. As it demands data, you have to provide anything as x - it can be NA, or some data. If you provide actual data, the plot function will figure out the plot margins from the data, otherwise you have to choose the margins by hand using xlim and ylim arguments. Next, you use abline that has parameters a and b for intercept and slope (or h and v if you want just a horizontal or vertical line).
plot(x=NA, type="n", ylim=c(100, 250), xlim=c(0, 50),
xlab=expression(x[1]), ylab=expression(hat(y)))
abline(a=108, b=0.2, col="red")
abline(a=101, b=2.15, col="green")
abline(a=132, b=0.2, col="blue")
abline(a=119, b=8.15, col="orange")
I am using the sm package in R to draw a density plot of several variables with different sample sizes, like this:
var1 <- density(vars1[,1])
var2 <- density(vars2[,1])
var3 <- density(vars3[,1])
pdf(file="density.pdf",width=8.5,height=8)
plot(var1,col="BLUE")
par(new=T)
plot(var2,axes=FALSE,col="RED")
par(new=T)
plot(var3,axes=FALSE,col="GREEN")
dev.off()
The problem I'm having, is that I want the y-axis to show the proportions so I can compare the different variables with each other in a more meaningful way. The maxima of all three density plots are now exactly the same, and I'm pretty sure that they wouldn't be if the y-axis showed proportions. Any suggestions? Many thanks!
Edit:
I just learned that I should not plot on top of an existing plot, so now the plotting part of the code looks like this:
pdf(file="density.pdf",width=8.5,height=8)
plot(var1,col="BLUE")
lines(var2,col="RED")
lines(var3,col="GREEN")
dev.off()
The maxima of those lines however are now very much in line with the sample size differences. Is there a way to put the proportions on the y-axis for all three variables, so the area under the curve is equal for all three variables? Many thanks!
Don't plot on top of an existing plot, because they axes may be different. Instead, use lines() to plot the second and third densities after plotting the first. If necessary, adjust the ylim parameter in plot() so that they all fit.
An example for how sample size ought not matter:
set.seed(1)
D1 <- density(rnorm(1000))
D2 <- density(rnorm(10000))
D3 <- density(rnorm(100000))
plot(D1$x,D1$y,type='l',col="red",ylim=c(0,.45))
lines(D2$x,D2$y,lty=2,col="blue")
lines(D3$x,D3$y,lty=3,col="green")
You could make tim's solution a little more flexible by not hard-coding in the limits.
plot(D1$x,D1$y,type='l',col="red",ylim=c(0, max(sapply(list(D1, D2, D3),
function(x) {max(x$y)}))))
This would also cater for Vincent's point that the density functions are not necessarily constrained in their range.
This question already has an answer here:
How To Avoid Density Curve Getting Cut Off In Plot
(1 answer)
Closed 6 years ago.
newbie here. I have a script to create graphs that has a bit that goes something like this:
png(Test.png)
ht=hist(step[i],20)
curve(insert_function_here,add=TRUE)
I essentially want to plot a curve of a distribution over an histogram. My problem is that the axes limits are apparently set by the histogram instead of the curve, so that the curve sometimes gets out of the Y axis limits. I have played with par("usr"), to no avail. Is there any way to set the axis limits based on the maximum values of either the histogram or the curve (or, in the alternative, of the curve only)?? In case this changes anything, this needs to be done within a for loop where multiple such graphs are plotted and within a series of subplots (par("mfrow")).
Inspired by other answers, this is what i ended up doing:
curve(insert_function_here)
boundsc=par("usr")
ht=hist(A[,1],20,plot=FALSE)
par(usr=c(boundsc[1:2],0,max(boundsc[4],max(ht$counts))))
plot(ht,add=TRUE)
It fixes the bounds based on the highest of either the curve or the histogram.
You could determine the mx <- max(curve_vector, ht$counts) and set ylim=(0, mx), but I rather doubt the code looks like that since [] is not a proper parameter passing idiom and step is not an R plotting function, but rather a model selection function. So I am guessing this is code in Matlab or some other idiom. In R, try this:
set.seed(123)
png("Test.png")
ht=hist(rpois(20,1), plot=FALSE, breaks=0:10-0.1)
# better to offset to include discrete counts that would otherwise be at boundaries
plot(round(ht$breaks), dpois( round(ht$breaks), # plot a Poisson density
mean(ht$counts*round(ht$breaks[-length(ht$breaks)]))),
ylim=c(0, max(ht$density)+.1) , type="l")
plot(ht, freq=FALSE, add=TRUE) # plot the histogram
dev.off()
You could plot the curve first, then compute the histogram with plot=FALSE, and use the plot function on the histogram object with add=TRUE to add it to the plot.
Even better would be to calculate the the highest y-value of the curve (there may be shortcuts to do this depending on the nature of the curve) and the highest bar in the histogram and give this value to the ylim argument when plotting the histogram.