I know that in base R I can use par(lheight = ...) to adjust the space between two lines that are broken by \n, e.g. in axis labels. I was wondering how to adjust the space between lines in multi-line vertex labels in igraph. I could not find an answer here on Stack Overflow, I hope I have not overseen anything obvious. Or is there any other option to break lines than using \n in igraph that allows to adjust the space?
library(igraph)
relations <- data.frame(from=c("Bob\nSurname", "Cecil\nSurname", "Cecil\nSurname", "David\nSurname",
"David\nSurname", "Esmeralda\nSurname"),
to=c("Alice\nSurname", "Bob\nSurname", "Alice\nSurname", "Alice\nSurname", "Bob\nSurname", "Alice\nSurname"))
g <- graph_from_data_frame(relations, directed=FALSE)
plot(g)
# this does not change the space between first name and surname...
par(lheight = .1)
plot(g)
Your solution works perfectly. Maybe you tried to see too small difference? Here you see that igraph plotting is built on top of basic R graphics. Labels are plotted by calling text() (near line 394) which takes the lheight parameter either from par or as argument in ... (here not used). Therefore setting par(lheight = x) must determine the label line height. Testing with your graph:
png('graph_lheight1.png')
par(lheight = 1)
plot(g)
dev.off()
png('graph_lheight2.png')
par(lheight = 2)
plot(g)
dev.off()
Related
I have a pppmatching object to plot (using the base plot function) where I need to change the thickness of line segments. Unfortunately, it seems that lwd is somehow changing the thickness of the plot border instead of the line segments! I am puzzled how to get around this.
Reproducible example:
library(spatstat)
set.seed(140627)
X <- runifpoint(300)
Y <- runifpoint(500)
m <- pppdist(X, Y)
m
plot(m, lwd = 5) #???
## End(Not run)
This is a question about the plot method plot.pppmatching in the package spatstat. This function plots line segments between the data points, with varying line widths proportional to the matching strength. The parameter lwd would override this behaviour and would plot all lines with the same width, defeating the purpose.
Text added later:
Perhaps you wanted to rescale the line widths, multiplying/dividing them by 5 to make them more visible? This can be done using the argument adjust. For example adjust=0.2 would divide all line widths by 5.
(In a future release of spatstat I will improve the documentation and suppress the irrelevant warning message)
I have a graph in igraph with a vertex size that is coded for a value.
I wish to add a legend with symbols (points in my case) with different sizes.
I tried the following code
require(igraph)
er_graph <- erdos.renyi.game(100, 5/100)
value<-runif(100)
n<-6
size_vec<-seq_len(n)
sizeCut<-cut(value,n)
vertex.size<-size_vec[sizeCut]
plot(er_graph, vertex.label=NA, vertex.size=vertex.size)
legend('topleft',legend=levels(sizeCut),pt.cex=size_vec,col='black')
but end with legend without symbols
see example
Any sugestions how I go about this?
You should set pch to some value to indicate which character you want to use for the bullets (see ?points to check the possible values).
Also, you should scale the pt.cex values in order to make the bullets not too big for the legend, and use pt.bg to set the background color of the bullets, e.g.
# scaled between 1 and 2
scaled <- 1 + ((2-1) * (size_vec - min(size_vec) ) / ( max(size_vec) - min(size_vec) ) )
legend('topleft',legend=levels(sizeCut),pt.cex=scaled,col='black',pch=21, pt.bg='orange')
EDIT :
Unfortunately, calculating the right sizes of the bullets is not easy; a possible workaround is plotting white bullets then manually add the vertices to the legend in the same way they are plotted inside the plot.igraph function :
# N.B. /200 is taken from plot.igraph source code
a <- legend('topleft',legend=levels(sizeCut),pt.cex=size_vec/200,col='white',
pch=21, pt.bg='white')
x <- (a$text$x + a$rect$left) / 2
y <- a$text$y
symbols(x,y,circles=size_vec/200,inches=FALSE,add=TRUE,bg='orange')
Disclaimer: this code heavily relies on the source code of plot.igraph function that might be changed in a future version of igraph. Probably you should search for another plot function for igraph which natively allows to add a legend.
Anyone looking at this who wants a continuous scale for node sizes instead of a discrete scale then this is the code you need to do it:
require(igraph)
er_graph <- erdos.renyi.game(100, 5/100)
value<-runif(100)
sizeCut<- c(0.2,0.4,0.6,0.8,1.0)
sizeCutScale <- sizeCut*10
vertex.size<-value*10
plot(er_graph, vertex.label=NA, vertex.size=vertex.size)
legend('topleft',legend=unique(sizeCut),pt.cex= sizeCutScale,col='black')
a <- legend('topleft',legend=unique(sizeCut),pt.cex=sizeCutScale/200,col='white',
pch=21, pt.bg='white')
x <- (a$text$x + a$rect$left) / 2
y <- a$text$y
symbols(x,y,circles=sizeCutScale/200,inches=FALSE,add=TRUE,bg='orange')
I have a graph that I have produced using igraph. I'd like to spread out the nodes. The only way I have found so far to do this is to scale the layout and force the plot command to not rescale.
png("kmeansColouredNetwork.png", width=1200,height = 1000)
col=c("yellow", "saddlebrown", "brown1","chartreuse2", "chocolate1","darkorange" ,"deepskyblue1", "hotpink1","plum2")
for(i in 1:9){
V(graph)$cluster[which(V(graph)$name %in% kmeans[,i])]<-col[i]
}
V(graph)$color=V(graph)$cluster
coords <- layout.fruchterman.reingold(graph)*0.5
plot(graph, layout = coords, vertex.label=NA, rescale=FALSE, vertex.size=degree(graph)*.25,vertex.color=V(graph)$cluster)
labels = paste("cluster:", 1:length(colours))
legend("left",legend=labels, col=col, pch=16, title="K means clustered subgroups")
dev.off()
If I don't rescale, the central highly connected nodes clump together and I get a graph like this, where the patterns in the body of the graph are impossible to discern:
On the other hand, if I tell the plot command not to rescale, then I get this :
where the patterns are discernible, but half the graph is off the plot. It's not a matter of plot size as if I increase the dimensions of the png, it still centres the graph off the edge of the plot.
It's not a matter of the layout - I've tried fruchterman.reingold, layout_nicely, reingold.tilford, layout.circle, layout random, the same thing happens.
There apparently used to be a variable to set a repulsion factor between nodes, but that appears to be deprecated.
How does one spread the nodes of the graph out or rescale and recenter the plot?
Option 1: make the vertices smaller
node.size= c(10,10,10)
plot(net, vertex.size=node.size*0.25)
Option 2 (in case the distances between the vertices are not important to you):
# Use the tkplot option to edit your graph in GUI
tkplot (net)
Note: tkplot outputs the graph as eps. If you want to edit it further or export it to pdf I suggest using inkscape (I use it for all my graph editing - just save the graph as pdf in RStudio and edit it in inkscape).
For the case of eps if you are on a windows machine you will need to tweak inkscape to open this format. A very short and simple process which is detailed here:
I just found the below answer on StackOverflow:
igraph axes xlim ylim plot incorrectly
Basically, you can set ylim and xlim and asp. You can set which part of the graph to display (as usual with xlim and ylim) and if the two axis are dependent on each other.
plot(g, rescale = FALSE, ylim=c(1,4),xlim=c(-17,24), asp = 0)
I'm trying to create a scatter plot + linear regression line in R 3.0.3. I originally tried to create it with the following simple call to plot:
plot(hops$average.temperature, hops$percent.alpha.acids)
This created this first plot:
As you can see, the scales of the Y and X axes differ. I tried fixing this using the asp parameter, as follows:
plot(hops$average.temperature, hops$percent.alpha.acids, asp=1, xaxp=c(13,18,5))
This produced this second plot:
Unfortunately, setting asp to 1 appears to have compressed the X axis while using the same amount of space, leaving large areas of unused whitespace on either side of the data. I tried using xlim to constrain the size of the X-axis, but asp seemed to overrule it as it didn't have any effect on the plot.
plot(hops$average.temperature, hops$percent.alpha.acids, xlim=c(13,18), asp=1, xaxp=c(13,18,5))
Any suggestions as to how I could get the axes to be on the same scale without creating large amounts of whitespace?
Thanks!
One solution would be to use par parameter pty and set it to "s". See ?par:
pty
A character specifying the type of plot region to be used; "s"
generates a square plotting region and "m" generates the maximal
plotting region.
It forces the plot to be square (thus conteracting the side effect of asp).
hops <- data.frame(a=runif(100,13,18),b=runif(100,2,6))
par(pty="s")
plot(hops$a,hops$b,asp=1)
I agree with plannapus that the issue is with your plotting area. You can also fix this within the device size itself by ensuring that you plot to a square region. The example below opens a plotting device with square dimension; then the margins are also set to maintain these proportions:
Example:
n <- 20
x <- runif(n, 13, 18)
y <- runif(n, 2, 6)
png("plot.png", width=5, height=5, units="in", res=200)
par(mar=c(5,5,1,1))
plot(x, y, asp=1)
dev.off()
The R package wordcloud has a very useful function which is called wordlayout. It takes initial positions of words and their respective sizes an rearranges them in a way that they do not overlap. I would like to use the results of this functions to do a geom_text plot in ggplot.
I came up with the following example but soon realized that there seems to be a big difference betweetn cex (wordlayout) and size (geom_plot) since words in graphics package appear way larger.
here is my sample code. Plot 1 is the original wordcloud plot which has no overlaps:
library(wordcloud)
library(tm)
library(ggplot2)
samplesize=100
textdf <- data.frame(label=sample(stopwords("en"),samplesize,replace=TRUE),x=sample(c(1:1000),samplesize,replace=TRUE),y=sample(c(1:1000),samplesize,replace=TRUE),size=sample(c(1:5),samplesize,replace=TRUE))
#plot1
plot.new()
pdf(file="plot1.pdf")
textplot(textdf$x,textdf$y,textdf$label,textdf$size)
dev.off()
#plot2
ggplot(textdf,aes(x,y))+geom_text(aes(label = label, size = size))
ggsave("plot2.pdf")
#plot3
new_pos <- wordlayout(x=textdf$x,y=textdf$y,words=textdf$label,cex=textdf$size)
textdf$x <- new_pos[,1]
textdf$y <- new_pos[,2]
ggplot(textdf,aes(x,y))+geom_text(aes(label = label, size = size))
ggsave("plot3.pdf")
#plot4
textdf$x <- new_pos[,1]+0.5*new_pos[,3]#this is the way the wordcloud package rearranges the positions. I took this out of the textplot function
textdf$y <- new_pos[,2]+0.5*new_pos[,4]
ggplot(textdf,aes(x,y))+geom_text(aes(label = label, size = size))
ggsave("plot4.pdf")
is there a way to overcome this cex/size difference and reuse wordlayout for ggplots?
cex stands for character expansion and is the factor by which text is magnified relative the default, specified by cin - set on my installation to 0.15 in by 0.2 in: see ?par for more details.
#hadley explains that ggplot2 sizes are measured in mm. Therefore cex=1 would correspond to size=3.81 or size=5.08 depending on if it is being scaled by the width or height. Of course, font selection may cause differences.
In addition, to use absolute sizes, you need to have the size specification outside the aes otherwise it considers it a variable to map to and choose the scale itself, eg:
ggplot(textdf,aes(x,y))+geom_text(aes(label = label),size = textdf$size*3.81)
Sadly I think you're going to find the short answer is no! I think the package handles the text vector mapping differently from ggplot2, so you can tinker with size and font face/family, etc. but will struggle to replicate exactly what the package is doing.
I tried a few things:
1) Try to plot the grobs from textdata using annotation_custom
require(plyr)
require(grid)
# FIRST TRY PLOT INDIVIDUAL TEXT GROBS
qplot(0:1000,0:1000,geom="blank") +
alply(textdf,1,function(x){
annotation_custom(textGrob(label=x$label,0,0,c("center","center"),gp=gpar(cex=x$size)),x$x,x$x,x$y,x$y)
})
2) Run the wordlayout() function which should readjust the text, but difficult to see for what font (similarly doesn't work)
# THEN USE wordcloud() TO GET CO-ORDS
plot.new()
wordlayout(textdf$x,textdf$y,words=textdf$label,cex=textdf$size,xlim=c(min(textdf$x),max(textdf$x)),ylim=c(min(textdf$y),max(textdf$y)))
plotdata<-cbind(data.frame(rownames(w)),w)
colnames(plotdata)=c("word","x","y","w","h")
# PLOT WORDCLOUD DATA
qplot(0:1000,0:1000,geom="blank") +
alply(plotdata,1,function(x){
annotation_custom(textGrob(label=x$word,0,0,c("center","center"),gp=gpar(cex=x$h*40)),x$x,x$x,x$y,x$y)
})
Here's a cheat if you just want to overplot other ggplot functions on top of it (although the co-ords don't seem to match up exactly between the data and the plot). It basically images the wordcloud, removes the margins, and under-plots it at the same scale:
# make a png file of just the panel
plot.new()
png(filename="bgplot.png")
par(mar=c(0.01,0.01,0.01,0.01))
textplot(textdf$x,textdf$y,textdf$label,textdf$size,xaxt="n",yaxt="n",xlab="",ylab="",asp=1)
dev.off()
# library to get PNG file
require(png)
# then plot it behind the panel
qplot(0:1000,0:1000,geom="blank") +
annotation_custom(rasterGrob(readPNG("bgplot.png"),0,0,1,1,just=c("left","bottom")),0,1000,0,1000) +
coord_fixed(1,c(0,1000),c(0,1000))