How to bind observations from a function with multiple arguments into rows? - r

I want to do a simulation to prove something. Therefore I created a function that generates one observation, very similar to this below.
set.seed(458007)
fun1 <- function(M, x, y){
M <- matrix(NA, nrow = 1, ncol = 4)
M[, 1] <- x
M[, 2] <- y
M[, 3] <- mean(rnorm(x, 0, y))
M[, 4] <- mean(rnorm(x, 1, y))
return(M)
}
fun1(x=1e3, y=.5)
# [,1] [,2] [,3] [,4]
# [1,] 1000 0.5 0.001414806 0.9875602
For the simulation now I want to bind repeated observations with different arguments into rows and chose an lapply() approach. Though I brought following code to work, it is ironically the slowest one. Note that in a related question I slightly overminimalized the example of my problem which is actually somewhat nested. Perhaps anyone could help me to find a faster solution?
# lapply
do.call(rbind, lapply(c(.5, 1, 1.5), function(y)
do.call(rbind, lapply(c(1e3, 1e4, 1e5),
function(x) do.call(rbind, lapply(1:5, fun1, x, y))))))
# [,1] [,2] [,3] [,4]
# [1,] 1e+03 0.5 0.0156969547 0.9878933
# [2,] 1e+03 0.5 0.0187202908 1.0011313
# [3,] 1e+03 0.5 -0.0351017539 0.9953701
# [4,] 1e+03 0.5 -0.0129749736 1.0112514
# [5,] 1e+03 0.5 -0.0154776052 0.9793552
# [6,] 1e+04 0.5 -0.0070121049 1.0022838
# [7,] 1e+04 0.5 -0.0064961931 0.9967966
# [8,] 1e+04 0.5 -0.0054208002 0.9955582
# [9,] 1e+04 0.5 -0.0027074479 1.0019217
# [10,] 1e+04 0.5 0.0047017946 1.0069838
# [11,] 1e+05 0.5 -0.0018550320 0.9981459
# [12,] 1e+05 0.5 -0.0019201731 0.9973762
# [13,] 1e+05 0.5 -0.0031555017 1.0016808
# [14,] 1e+05 0.5 -0.0005508661 1.0001200
# [15,] 1e+05 0.5 0.0002928878 0.9991147
# [16,] 1e+03 1.0 0.0043441072 0.9579204
# [17,] 1e+03 1.0 -0.0059409534 1.0068553
# [18,] 1e+03 1.0 0.0850053171 1.0316056
# [19,] 1e+03 1.0 -0.0145192268 1.0193467
# [20,] 1e+03 1.0 0.0104437603 0.9959815
# [21,] 1e+04 1.0 0.0252303898 0.9968866
# [22,] 1e+04 1.0 0.0039449755 0.9818866
# [23,] 1e+04 1.0 0.0145974970 0.9814802
# [24,] 1e+04 1.0 -0.0016105680 0.9968357
# [25,] 1e+04 1.0 0.0058877101 1.0049794
# [26,] 1e+05 1.0 0.0015416062 1.0008094
# [27,] 1e+05 1.0 0.0004725605 1.0001917
# [28,] 1e+05 1.0 -0.0007963141 1.0019771
# [29,] 1e+05 1.0 -0.0007302225 0.9969158
# [30,] 1e+05 1.0 0.0023877190 1.0060436
# [31,] 1e+03 1.5 0.0165765473 0.9391917
# [32,] 1e+03 1.5 -0.0990828503 1.0256720
# [33,] 1e+03 1.5 0.0526152728 0.9981981
# [34,] 1e+03 1.5 0.1472273215 0.9442844
# [35,] 1e+03 1.5 0.0346540383 1.0316669
# [36,] 1e+04 1.5 -0.0007479431 0.9800219
# [37,] 1e+04 1.5 0.0189053160 1.0284075
# [38,] 1e+04 1.5 0.0062155928 0.9821324
# [39,] 1e+04 1.5 -0.0065533501 1.0085699
# [40,] 1e+04 1.5 -0.0161694486 1.0126392
# [41,] 1e+05 1.5 -0.0090145992 0.9952551
# [42,] 1e+05 1.5 -0.0024756213 1.0054282
# [43,] 1e+05 1.5 0.0061985946 0.9966108
# [44,] 1e+05 1.5 0.0023640342 0.9988624
# [45,] 1e+05 1.5 0.0014610948 0.9956877
Benchmark with #lefft's and #Parfait's solutions (from example)
# Unit: milliseconds
# expr min lq mean median uq max neval
# mine 325.8589 347.1616 405.6944 398.6682 434.9392 906.7906 100
# lefft 327.6870 348.3504 393.7511 393.2127 421.4536 694.1610 100
# Parfait 323.5595 343.5806 396.9973 390.9864 423.0759 736.2887 100

Here is a nice compact way to do it:
# create the input data *before* applying the func to it
dat <- expand.grid(Ms=1:5, xs=c(1e3, 1e4, 1e5), ys=c(.5, 1, 1.5))
# apply the function to each row of the data frame (`MARGIN=1` is row-wise)
# (the outermost function `t()` transposes the result so it looks like yours)
t(apply(dat, MARGIN=1, FUN=function(row) fun1(M=row[1], x=row[2], y=row[3])))
It is about 10% faster than the original solution, but the difference might be different with bigger data. One thing to note is that if you create the param grid first (as is done here), then the time spent on computing with fun1() can be isolated more easily (i.e. you can tell what's taking a long time -- the computing or the creation of the input data frame).
Hope this helps!

Related

Local Polynomial Regression: Reading nearest neighbor smoothing constants from R code

I'm studying from a textbook on data mining and I can't figure out how the author reads the nn values from the gcv output. The code and output are below:
## cv
alpha <- seq(0.20, 1, by = 0.01)
n1 = length(alpha)
g = matrix(nrow = n1, ncol = 4)
for (k in 1:length(alpha)) {
g[k,] <- gcv(NOx ~ lp(EquivRatio, nn = alpha[k]), data = ethanol)
}
g
the csv file is here:
https://github.com/jgscott/ECO395M/blob/master/data/ethanol.csv
I'm usin locfit library in R.
How do you find with given output?
The nn values are not read from the output - they are given in the input. In the loop, nn is assigned as the kth value of the object alpha.
Let's look at the output of the first 16 rows of g, which is the same as the picture you included in your question:
g[1:16,]
#> [,1] [,2] [,3] [,4]
#> [1,] -3.220084 18.81266 16.426487 0.1183932
#> [2,] -3.249601 17.61614 15.436227 0.1154507
#> [3,] -3.319650 16.77004 14.752039 0.1151542
#> [4,] -3.336464 15.44404 13.889209 0.1115457
#> [5,] -3.373011 14.52391 13.115430 0.1099609
#> [6,] -3.408908 13.96789 12.634934 0.1094681
#> [7,] -3.408908 13.96789 12.634934 0.1094681
#> [8,] -3.469254 12.99316 11.830996 0.1085293
#> [9,] -3.504310 12.38808 11.283837 0.1078784
#> [10,] -3.529167 11.93838 10.928859 0.1073628
#> [11,] -3.546728 11.46960 10.516520 0.1065792
#> [12,] -3.552238 11.26372 10.322329 0.1061728
#> [13,] -3.576083 11.03575 10.135243 0.1062533
#> [14,] -3.679128 10.54096 9.662613 0.1079229
#> [15,] -3.679128 10.54096 9.662613 0.1079229
#> [16,] -3.699044 10.46534 9.578396 0.1082955
Note that rows 11, 12 and 13 were created inside your loop using alpha[11], alpha[12] and alpha[13]. These values were passed to the nn argument of lp. If you want the nn values included in your table, all you need to do is:
cbind(g, nn = alpha)
#> nn
#> [1,] -3.220084 18.812657 16.426487 0.1183932 0.20
#> [2,] -3.249601 17.616143 15.436227 0.1154507 0.21
#> [3,] -3.319650 16.770041 14.752039 0.1151542 0.22
#> [4,] -3.336464 15.444040 13.889209 0.1115457 0.23
#> [5,] -3.373011 14.523910 13.115430 0.1099609 0.24
#> [6,] -3.408908 13.967891 12.634934 0.1094681 0.25
#> [7,] -3.408908 13.967891 12.634934 0.1094681 0.26
#> [8,] -3.469254 12.993165 11.830996 0.1085293 0.27
#> [9,] -3.504310 12.388077 11.283837 0.1078784 0.28
#> [10,] -3.529167 11.938379 10.928859 0.1073628 0.29
#> [11,] -3.546728 11.469598 10.516520 0.1065792 0.30
#> [12,] -3.552238 11.263716 10.322329 0.1061728 0.31
#> [13,] -3.576083 11.035752 10.135243 0.1062533 0.32
#> [14,] -3.679128 10.540964 9.662613 0.1079229 0.33
#> [15,] -3.679128 10.540964 9.662613 0.1079229 0.34
#> [16,] -3.699044 10.465337 9.578396 0.1082955 0.35

How to map numeric vector to hex color codes in R

I'm going to provide an example to make this a little easier. Let's say I have a numeric vector ranging from -2 to +2. I would like to map the numeric values to a color hex code. The colors closer to -2 would be red and the colors close to +2 would be blue. Numeric values close to zero would be grey. So for example the vector below
x <- c(-2,0,2)
would become
x <- c("#FF5733","#8E8E8E","#355EDF")
Obviously I am going to have many numbers between -2 and +2 which is where I am having the issue. Any help appreciated.
You can use colorRamp and rgb:
colfunc <- colorRamp(c("#ff5733", "#838383", "#355edf"))
cols <- colfunc(seq(0,1,len=11))
cols
# [,1] [,2] [,3]
# [1,] 255.0 87.0 51.0
# [2,] 230.2 95.8 67.0
# [3,] 205.4 104.6 83.0
# [4,] 180.6 113.4 99.0
# [5,] 155.8 122.2 115.0
# [6,] 131.0 131.0 131.0
# [7,] 115.4 123.6 149.4
# [8,] 99.8 116.2 167.8
# [9,] 84.2 108.8 186.2
# [10,] 68.6 101.4 204.6
# [11,] 53.0 94.0 223.0
rgb(cols[,1], cols[,2], cols[,3], maxColorValue = 255)
# [1] "#FF5733" "#E65F43" "#CD6852" "#B47163" "#9B7A72" "#838383" "#737B95" "#6374A7" "#546CBA" "#4465CC" "#355EDF"
plot(1:11, rep(1, 11), col=rgb(cols[,1], cols[,2], cols[,3], maxColorValue = 255), pch=16, cex=3)
The colorRamp returns a function, to which you should pass normalized values (on [0,1]), where 0 prefers the first color and 1 the last. (This means you are responsible for scaling from c(-2,0,2) to c(0,0.5,1).)

Subsetting for matrices

I'm playing around with some data, my matrix looks as shown in results:
results
[,1] [,2] [,3]
[1,] 1.7 0.0015 -1566.276
[2,] 1.7 0.0016 -1564.695
[3,] 1.7 0.0017 -1564.445
[4,] 1.7 0.0018 -1565.373
[5,] 1.7 0.0019 -1567.352
[6,] 1.7 0.0020 -1570.274
[7,] 1.8 0.0015 -1568.299
[8,] 1.8 0.0016 -1565.428
[9,] 1.8 0.0017 -1563.965
[10,] 1.8 0.0018 -1563.750
[11,] 1.8 0.0019 -1564.647
[12,] 1.8 0.0020 -1566.544
[13,] 1.9 0.0015 -1571.798
[14,] 1.9 0.0016 -1567.635
[15,] 1.9 0.0017 -1564.960
[16,] 1.9 0.0018 -1563.602
[17,] 1.9 0.0019 -1563.418
[18,] 1.9 0.0020 -1564.289
[19,] 2.0 0.0015 -1576.673
[20,] 2.0 0.0016 -1571.220
[21,] 2.0 0.0017 -1567.332
[22,] 2.0 0.0018 -1564.831
[23,] 2.0 0.0019 -1563.566
[24,] 2.0 0.0020 -1563.410
I wanted to print the row where the maximum on the 3rd column occurs, so I split it down in the following 2 lines:
max(results[,3])
results[results[,3] == -1563.41,]
The result didn't give me the desired row.
I tried putting the value in inverted commas as so:
max(results[,3])
results[results[,3] == "-1563.41",]
but this didn't work either.
the only code which worked was when I nested the max line of code in the 2nd line of code as so:
results[results[,3] == max(results[,3]),]
could someone please explain why breaking it down into steps didn't work??
I tried turning it into a data frame which didn't work either. using the filter from the tidyverse package didn't work either.
thanks,
R prints numbers to a maximum of getOption("digits") significant digits. I suspect that -1563.410 is the maximum rounded to seven significant digits, not the exact maximum. If no element of results[, 3] is exactly equal to the rounded number -1563.410, then
results[results[, 3] == -1563.41, ]
returns a 0-by-3 matrix. Meanwhile, max(results[, 3]) is the exact maximum, so
results[results[, 3] == max(results[, 3]), ]
returns the matrix subset that you want.
Note that if there are n rows whose third element is equal to max(results[, 3]), then the subset is a length-3 vector if n = 1 and an n-by-3 matrix if n > 1 (see ?Extract). If, in the n > 1 case, you only want the first of the n rows, then you should do:
results[which.max(results[, 3]), ]

Sampling random values (coordinates) within error range

In R is there a way to randomly generate values within a set extent from a given point.
For example if I have coordinates and wish to generate 10 samples within an surrounding error field, can this be done? And if so, can the characteristics of the error field be defined, i.e a square or circle surrounding the original point. Any insight much appreciated.
Example:(WGS84 ESPG:4326)
Longitude Latitude ErrLong ErrLat
-91.98876953 1.671900034 0.53 1.08
-91.91790771 1.955003262 0.53 1.08
-91.91873169 1.961261749 0.53 1.08
-91.86060333 1.996331811 0.53 1.08
-91.67115021 1.929548025 0.12 0.12
-90.67552948 1.850875616 0.12 0.12
-90.65361023 1.799352288 0.12 0.12
-92.13287354 0.755102754 0.12 0.12
-92.13739014 0.783674061 0.12 0.12
-88.16407776 -4.953748703 0.12 0.12
-82.51725006 -5.717019081 0.12 0.12
-82.50763702 -5.706347942 0.12 0.12
-82.50556183 -5.696153641 0.12 0.12
-82.50305176 -5.685819626 0.12 0.12
-82.18003845 -5.623015404 0.53 1.08
-82.17269897 -5.61870575 0.53 1.08
-82.16355133 -5.612465382 0.12 0.12
For each long/lat I would like 10 randomly generated points within the stated error long/lat (in degrees) from the original location. The random samples should follow a normal distribution and the error field is circular (when lat/long error is equal) and elliptical if not.
You could draw from a truncated normal using msm::rtnorm.
First, to make things easier, I'd convert the data into long format.
dat <- cbind(id=1:nrow(dat), dat) ## add ID column
names(dat)[-1] <- c("value.lon", "value.lat", "err.lon", "err.lat") ## better names
## reshape to long
dat.l <- reshape(dat, varying=2:5, direction="long")
dat.l[c(1:2, 15:20), ]
# id time value err
# 1.lon 1 lon -91.988770 0.53
# 2.lon 2 lon -91.917908 0.53
# 15.lon 15 lon -82.180038 0.53
# 16.lon 16 lon -82.172699 0.53
# 1.lat 1 lat 1.671900 1.08
# 2.lat 2 lat 1.955003 1.08
# 3.lat 3 lat 1.961262 1.08
# 4.lat 4 lat 1.996332 1.08
Now we use msm::rtnorm taking value as the mean and err as the absolute value of a confidence interval as well as the truncation points. To get the list nicely separated into lon and lat we use by.
R. <- 1e3
set.seed(42)
res <- by(dat.l, dat.l$time, function(s)
sapply(1:nrow(s), function(m, R=R.) {
x <- as.double(unlist(s[m, -(1:2)]))
o <- msm::rtnorm(R, x[1], abs((x[1] - x[2]))/1.96, x[1] - x[2], x[1] + x[2])
}))
Result
The result looks like this (using R. <- 9) for sake of brevity:
res
# dat.l$time: lat
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
# [1,] 2.059389 2.854458 1.6480049 1.578799 1.857519 1.933703 1.693664 0.6670599 0.7215978
# [2,] 1.817794 2.435360 0.9810172 1.433516 1.820929 1.844537 1.722964 0.7541789 0.7772778
# [3,] 1.363776 1.499776 2.3656603 2.753531 1.951757 1.911148 1.755089 0.6590040 0.8097877
# [4,] 1.298948 2.903252 1.3621228 2.685882 1.902042 1.850533 1.824228 0.6813604 0.7081114
# [5,] 1.976920 2.017745 2.1074160 2.823800 1.950198 1.785133 1.762703 0.7199149 0.8322832
# [6,] 1.664815 1.664443 1.6482465 1.441457 1.899035 1.807138 1.810606 0.7456769 0.8074188
# [7,] 1.736728 1.494439 2.2212244 1.744971 1.987707 1.835817 1.878827 0.7938251 0.8730894
# [8,] 1.518350 1.541916 1.9629348 1.386725 1.985631 1.833966 1.809587 0.7365271 0.7162421
# [9,] 1.761203 1.667451 1.7359951 2.712280 1.849972 1.965899 1.818468 0.8044030 0.7862688
# [,10] [,11] [,12] [,13] [,14] [,15] [,16]
# [1,] -4.909253 -5.611472 -5.673014 -5.688496 -5.668813 -5.117575 -6.365792
# [2,] -5.024007 -5.691572 -5.601893 -5.752438 -5.771032 -5.795218 -5.392146
# [3,] -4.959013 -5.636268 -5.791113 -5.639635 -5.670745 -5.902636 -4.946774
# [4,] -5.031824 -5.609281 -5.650881 -5.730072 -5.680132 -4.940293 -5.801787
# [5,] -4.984777 -5.774233 -5.807611 -5.711324 -5.801857 -4.618648 -5.821920
# [6,] -4.967051 -5.760783 -5.692485 -5.770230 -5.744132 -6.684446 -6.646540
# [7,] -4.929440 -5.648386 -5.798339 -5.728268 -5.669888 -5.140643 -6.525713
# [8,] -5.031480 -5.609127 -5.646710 -5.579407 -5.787876 -4.587991 -4.771850
# [9,] -5.071611 -5.763129 -5.621419 -5.606133 -5.592998 -6.402314 -4.752597
# ----------------------------------------------------------------------
# dat.l$time: lon
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
# [1,] -92.12306 -92.27813 -91.89380 -91.96530 -91.70359 -90.59310 -90.60037 -92.12645
# [2,] -92.08298 -91.73772 -91.74796 -92.32808 -91.57151 -90.55784 -90.69050 -92.11317
# [3,] -91.94673 -91.83403 -91.66878 -91.60644 -91.66306 -90.75866 -90.66495 -92.11768
# [4,] -92.33240 -91.57389 -92.15855 -92.03448 -91.75625 -90.63687 -90.58756 -92.11370
# [5,] -92.17743 -91.58370 -91.82970 -91.44922 -91.72398 -90.75778 -90.62202 -92.15861
# [6,] -92.39499 -91.41112 -92.36735 -92.12330 -91.78401 -90.68612 -90.56967 -92.05469
# [7,] -92.40120 -92.02109 -91.57844 -92.07230 -91.75370 -90.72048 -90.64158 -92.24910
# [8,] -92.08168 -92.10115 -91.98592 -91.33367 -91.58579 -90.60831 -90.65058 -92.17405
# [9,] -91.90599 -91.41466 -91.49233 -91.62150 -91.61410 -90.60368 -90.75319 -92.01950
# [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
# [1,] -92.16208 -88.17055 -82.51806 -82.50556 -82.54585 -82.49562 -81.76493 -81.84638
# [2,] -92.25042 -88.27982 -82.50876 -82.61386 -82.49595 -82.40652 -82.31069 -82.34158
# [3,] -92.20928 -88.08214 -82.55565 -82.43839 -82.48540 -82.55503 -82.38119 -81.84021
# [4,] -92.16342 -88.08550 -82.60778 -82.40032 -82.61227 -82.55625 -82.70171 -82.46027
# [5,] -92.02135 -88.09106 -82.44550 -82.51054 -82.54662 -82.40365 -81.91754 -81.83588
# [6,] -92.02523 -88.22512 -82.58183 -82.43660 -82.51187 -82.47769 -82.56931 -81.86314
# [7,] -92.18523 -88.27581 -82.51715 -82.45542 -82.40686 -82.59609 -81.75961 -82.62096
# [8,] -92.09482 -88.23731 -82.43151 -82.51785 -82.45835 -82.54335 -82.45329 -81.75484
# [9,] -92.07861 -88.18889 -82.60739 -82.46636 -82.48639 -82.41555 -82.11490 -82.59231
Check
Comparison with specified error ranges:
lapply(res, function(x) cbind(mean=colMeans(x), err=apply(x, 2, function(x)
max(abs(range(x - mean(x))))
)))
# $lat
# mean err
# [1,] 1.6641013 1.0633450
# [2,] 1.9512697 1.0791531
# [3,] 1.9664345 1.0766429
# [4,] 1.9827845 1.0752871
# [5,] 1.9284320 0.1210392
# [6,] 1.8525683 0.1213176
# [7,] 1.8010929 0.1214542
# [8,] 0.7511818 0.1237103
# [9,] 0.7871224 0.1228840
# [10,] -4.9542575 0.1203926
# [11,] -5.7174928 0.1200936
# [12,] -5.7064194 0.1198188
# [13,] -5.6925109 0.1234913
# [14,] -5.6876203 0.1217520
# [15,] -5.6436551 1.1001096
# [16,] -5.5955709 1.1015958
#
# $lon
# mean err
# [1,] -91.99891 0.5390560
# [2,] -91.91370 0.5327020
# [3,] -91.92065 0.5312584
# [4,] -91.84195 0.5476753
# [5,] -91.67497 0.1229412
# [6,] -90.67413 0.1212662
# [7,] -90.64743 0.1261391
# [8,] -92.13235 0.1204769
# [9,] -92.13511 0.1214228
# [10,] -88.16036 0.1235441
# [11,] -82.51747 0.1198272
# [12,] -82.50483 0.1225459
# [13,] -82.50418 0.1212391
# [14,] -82.50338 0.1202114
# [15,] -82.16850 0.5410282
# [16,] -82.16828 0.5330564
Looks not too bad.
And the distributions look like so (using R. <- 1e3):
Longitudes:
Latitudes:
Data:
dat <- read.table(header=TRUE, text='Longitude Latitude ErrLong ErrLat
-91.98876953 1.671900034 0.53 1.08
-91.91790771 1.955003262 0.53 1.08
-91.91873169 1.961261749 0.53 1.08
-91.86060333 1.996331811 0.53 1.08
-91.67115021 1.929548025 0.12 0.12
-90.67552948 1.850875616 0.12 0.12
-90.65361023 1.799352288 0.12 0.12
-92.13287354 0.755102754 0.12 0.12
-92.13739014 0.783674061 0.12 0.12
-88.16407776 -4.953748703 0.12 0.12
-82.51725006 -5.717019081 0.12 0.12
-82.50763702 -5.706347942 0.12 0.12
-82.50556183 -5.696153641 0.12 0.12
-82.50305176 -5.685819626 0.12 0.12
-82.18003845 -5.623015404 0.53 1.08
-82.17269897 -5.61870575 0.53 1.08')

r igraph - how does plot() read the layout matrix?

My question is related to this one here, which unfortunately has not been responded. I'm trying to automatically annotate text next to highlighted communities on a plot. An intermediate step is to understand how nodes are placed on a plot.
G <- make_graph('zachary')
l <- layout_with_fr(G)
l
A layout is a matrix with rows representing nodes and columns representing the x and y plot parameters.
[,1] [,2]
[1,] 2.8510654 -2.2404898
[2,] 2.7183497 -1.1815130
[3,] 3.1429205 0.1117099
[4,] 1.5585372 -1.0743325
[5,] 2.2808632 -4.2035479
[6,] 2.1698198 -5.0526766
[7,] 1.4938068 -4.6975884
[8,] 1.9710816 -1.4672218
[9,] 3.5407035 0.5407852
[10,] 2.2222909 1.9079805
[11,] 3.0784642 -4.5828448
[12,] 4.4115351 -4.1057462
[13,] 0.6002378 -2.2432049
[14,] 2.5010525 -0.1563341
[15,] 4.8914673 4.1417759
[16,] 3.2053338 3.9212694
[17,] 1.1825200 -6.4099021
[18,] 3.7155897 -2.8354432
[19,] 3.8272351 4.2660906
[20,] 3.8636487 -0.5671906
[21,] 2.7302411 3.3998888
[22,] 1.6084374 -2.7407388
[23,] 4.3432855 3.8101278
[24,] 5.9392042 2.2364929
[25,] 6.9980077 0.2389222
[26,] 7.1608499 1.1360134
[27,] 6.0171481 4.0279067
[28,] 5.4996627 1.0367163
[29,] 4.4961257 0.9434659
[30,] 5.5987563 3.2314488
[31,] 2.9958404 1.2022317
[32,] 5.1188900 0.2919268
[33,] 4.1088296 2.5032294
[34,] 4.1686534 2.1339884
But the x, y coordinates of the plot go from -1 to 1, unlike the min-max coordinates in the layout matrix. So how is plot(G, layout = l) reading the layout matrix?
The according to the source, the plot method for objects of class igraph simply rescales the matrix from -1 to 1.
library(igraph)
set.seed(3)
l <- layout_with_fr(G)
[,1] [,2]
[1,] -2.283 0.658
[2,] -1.289 -0.108
[3,] 0.146 1.012
[4,] -1.523 1.601
#... with 30 more rows.
plot(G,layout = l)
maxs <- apply(l, 2, max)
mins <- apply(l, 2, min)
ll <- scale(l, center=(maxs+mins)/2, scale=(maxs-mins)/2)
ll
[,1] [,2]
[1,] -0.2422 -0.1051
[2,] -0.0704 -0.3821
[3,] 0.1775 0.0228
[4,] -0.1108 0.2357
#... with 30 more rows.
plot(G,layout = ll)
Note that the actual rescaling is performed with igraph::norm_coords:
igraph::norm_coords(l)
[,1] [,2]
[1,] -0.2422 -0.1051
[2,] -0.0704 -0.3821
[3,] 0.1775 0.0228
[4,] -0.1108 0.2357
#... with 30 more rows.

Resources