我有一个问题,我有一堆长度,并希望从原点开始(假装我正面向y轴的正端),我向右移动并沿x轴正向移动length_i的距离.这时我又转了一圈,走了length_i的距离,重复了n次.我可以做到这一点,但我认为有一种更有效的方法,我缺乏数学背景:
## Fake Data set.seed(11) dat <- data.frame(id = LETTERS[1:6], lens=sample(2:9, 6), x1=NA, y1=NA, x2=NA, y2=NA) ## id lens x1 y1 x2 y2 ## 1 A 4 NA NA NA NA ## 2 B 2 NA NA NA NA ## 3 C 5 NA NA NA NA ## 4 D 8 NA NA NA NA ## 5 E 6 NA NA NA NA ## 6 F 9 NA NA NA NA ## Add a cycle of 4 column dat[, "cycle"] <- rep(1:4, ceiling(nrow(dat)/4))[1:nrow(dat)] ##For loop to use the information from cycle column for(i in 1:nrow(dat)) { ## set x1, y1 if (i == 1) { dat[1, c("x1", "y1")] <- 0 } else { dat[i, c("x1", "y1")] <- dat[(i - 1), c("x2", "y2")] } col1 <- ifelse(dat[i, "cycle"] %% 2 == 0, "x1", "y1") col2 <- ifelse(dat[i, "cycle"] %% 2 == 0, "x2", "y2") dat[i, col2] <- dat[i, col1] col3 <- ifelse(dat[i, "cycle"] %% 2 != 0, "x2", "y2") col4 <- ifelse(dat[i, "cycle"] %% 2 != 0, "x1", "y1") mag <- ifelse(dat[i, "cycle"] %in% c(1, 4), 1, -1) dat[i, col3] <- dat[i, col4] + (dat[i, "lens"] * mag) }
这给出了期望的结果:
> dat id lens x1 y1 x2 y2 cycle 1 A 4 0 0 4 0 1 2 B 2 4 0 4 -2 2 3 C 5 4 -2 -1 -2 3 4 D 8 -1 -2 -1 6 4 5 E 6 -1 6 5 6 1 6 F 9 5 6 5 -3 2
这是一个情节:
library(ggplot2); library(grid) ggplot(dat, aes(x = x1, y = y1, xend = x2, yend = y2)) + geom_segment(aes(color=id), size=3, arrow = arrow(length = unit(0.5, "cm"))) + ylim(c(-10, 10)) + xlim(c(-10, 10))
这似乎缓慢而笨重.我猜这是一个比我在for
循环中做的项目更好的方法.什么是一种更有效的方式来保持编程权利?
这是使用复数的另一种方法.您可以通过乘以在复平面中"向右"旋转矢量-1i
.下面的代码使第一次遍历进入正X(Re() - al轴)并且每次后续遍历将旋转到"右"
imVecs <- lengths*c(0-1i)^(0:3) imVecs # [1] 9+0i 0-5i -9+0i 0+9i 8+0i 0-5i -8+0i 0+7i 8+0i 0-1i -5+0i 0+3i 4+0i 0-7i -4+0i 0+2i #[17] 3+0i 0-7i -5+0i 0+8i cumsum(imVecs) # [1] 9+0i 9-5i 0-5i 0+4i 8+4i 8-1i 0-1i 0+6i 8+6i 8+5i 3+5i 3+8i 7+8i 7+1i 3+1i 3+3i 6+3i 6-4i 1-4i #[20] 1+4i plot(cumsum(imVecs)) lines(cumsum(imVecs))
这是使用复平面旋转向右旋转45度的方法:
> sqrt(-1i) [1] 0.7071068-0.7071068i > imVecs <- lengths*sqrt(0-1i)^(0:7) Warning message: In lengths * sqrt(0 - (0+1i))^(0:7) : longer object length is not a multiple of shorter object length > plot(cumsum(imVecs)) > lines(cumsum(imVecs))
情节:
(正如@DWin所建议的)这是一个使用复数的解决方案,它可以灵活地应用于任何类型turn
,而不仅仅是90度(-pi/2弧度)的直角.一切都是矢量化的:
set.seed(11) dat <- data.frame(id = LETTERS[1:6], lens = sample(2:9, 6), turn = -pi/2) dat <- within(dat, { facing <- pi/2 + cumsum(turn) move <- lens * exp(1i * facing) position <- cumsum(move) x2 <- Re(position) y2 <- Im(position) x1 <- c(0, head(x2, -1)) y1 <- c(0, head(y2, -1)) }) dat[c("id", "lens", "x1", "y1", "x2", "y2")] # id lens x1 y1 x2 y2 # 1 A 4 0 0 4 0 # 2 B 2 4 0 4 -2 # 3 C 5 4 -2 -1 -2 # 4 D 8 -1 -2 -1 6 # 5 E 6 -1 6 5 6 # 6 F 9 5 6 5 -3
该turn
变量应该被视为一个输入lens
.现在所有转弯都是-pi/2
弧度,但你可以将它们中的每一个设置为你想要的任何一个.所有其他变量都是输出.
现在玩得开心吧:
trace.path <- function(lens, turn) { facing <- pi/2 + cumsum(turn) move <- lens * exp(1i * facing) position <- cumsum(move) x <- c(0, Re(position)) y <- c(0, Im(position)) plot.new() plot.window(range(x), range(y)) lines(x, y) } trace.path(lens = seq(0, 1, length.out = 200), turn = rep(pi/2 * (-1 + 1/200), 200))
(我在这里复制图表的尝试:http://en.wikipedia.org/wiki/Turtle_graphics)
我也让你尝试这些:
trace.path(lens = seq(1, 10, length.out = 1000), turn = rep(2 * pi / 10, 1000)) trace.path(lens = seq(0, 1, length.out = 500), turn = seq(0, pi, length.out = 500)) trace.path(lens = seq(0, 1, length.out = 600) * c(1, -1), turn = seq(0, 8*pi, length.out = 600) * seq(-1, 1, length.out = 200))
随意添加你的!
这不是一个漂亮的情节,但我已经将它包括在内,以表明这种"矢量化"坐标计算产生了正确的结果,不应该太难以适应您的需求:
xx <- c(1,0,-1,0) yy <- c(0,-1,0,1) coords <- suppressWarnings(cbind(x = cumsum(c(0,xx*dat$lens)), y = cumsum(c(0,yy*dat$lens)))) plot(coords, type="l", xlim=c(-10,10), ylim=c(-10,10))