2013-12-03 4 views
13

У меня есть проблема, когда у меня есть куча длин и вы хотите начать с начала (притворяйтесь, что я смотрю на положительный конец оси 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. Что является более эффективным способом сохранения программных прав?

enter image description here

+1

Вы просто добавляете двухмерные векторы. (В R не существует двухмерного векторного класса, но для вас это может сделать либо матрица, либо комплексные числа. Попробуйте уменьшить (cumsum, ..., accumulate = TRUE) по координатам x и y –

ответ

10

(Как предложено @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)) 

enter image description here

(Моя попытка тиражирования график здесь: 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)) 

Не стесняйтесь добавлять свои!

+0

Кто-нибудь еще напомнил о «черепаховой графике»? –

+1

@DWin - у меня поднята рука;) На первый взгляд, я предполагаю ed Tyler начал внедрять [Logo] (http://en.wikipedia.org/wiki/Logo_%28programming_language%29) в R. –

+0

У меня появилось больше ответов, чем я ожидал. Все решения работали и должны учитываться будущими поисковиками. Для моих нужд это решение было наилучшим подходом. Спасибо вам всем. –

5

Это не очень сюжет, но я включил его, чтобы показать, что это «векторную» координируют расчет дает правильные результаты, которые не должны быть слишком трудно адаптироваться к вашим потребностям:

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)) 

enter image description here

1

Очень похоже на решение Джоша:

lengths <- sample(1:10, 20, repl=TRUE) 
x=cumsum(lengths*c(1,0,-1,0)) 
y=cumsum(lengths*c(0,1,0,-1)) 
cbind(x,y) 
     x y 
[1,] 9 0 
[2,] 9 5 
[3,] 0 5 
[4,] 0 -4 
[5,] 8 -4 
[6,] 8 1 
[7,] 0 1 
[8,] 0 -6 
[9,] 8 -6 
[10,] 8 -5 
[11,] 3 -5 
[12,] 3 -8 
[13,] 7 -8 
[14,] 7 -1 
[15,] 3 -1 
[16,] 3 -3 
[17,] 6 -3 
[18,] 6 4 
[19,] 1 4 
[20,] 1 -4 

Base график:

plot(cbind(x,y)) 
arrows(cbind(x,y)[-20,1],cbind(x,y)[-20,2], cbind(x,y)[-1,1], cbind(x,y)[-1,2]) 

enter image description here

Это подчеркивает тот факт, что оба Джоша и мои решения «поворот неправильного пути», так что вы должны изменить знаки на наши «переходных матрицах». И мы, вероятно, должны были начать с (0,0), но у вас не должно возникнуть проблем с адаптацией ваших потребностей.

3

Возможно, было бы полезно подумать об этом с точки зрения расстояния и опоры. Расстояние задается dat$lens, а подшипник угол перемещения относительно некоторой произвольной опорной линии (скажем, ось х). Затем, на каждом шаге,

x.new = x.old + distance * cos(bearing) 
y.new = y.old + distance * sin(bearing) 
bearing = bearing + increment 

Здесь, так как мы начинаем в начале координат и двигаться в направлении + х, (x,y)=(0,0) и подшипник начинается при 0 градусов. Правый поворот - это просто шаг приращения -90 градусов (-pi/2 радиан). Таким образом, в R кода, используя ваше определение dat:

x <-0 
y <- 0 
bearing <- 0 
for (i in 1:nrow(dat)){ 
    dat[i,c(3,4)] <- c(x,y) 
    length <- dat[i,2] 
    x <- x + length * cos(bearing) 
    y <- y + length * sin(bearing) 
    dat[i,c(5,6)] <- c(x,y) 
    bearing <- bearing - pi/2 
} 

Это производит то, что у вас и имеет то преимущество, что вы можете обновить его очень просто сделать повороты налево или 45 градусов поворачивает, или любой другой. Вы можете даже добавить столбец bearing.increment в dat, чтобы создать случайную прогулку.

+0

+1; аналогично комплексным числам, которые я предлагаю. Чтобы изменить аргумент несущей, вам просто нужно, чтобы серия векторов, умножающих векторы длины, имела длину один. –

+0

Да, но это также можно расширить до трех измерений (или n измерений для это не так.) Я не считаю, что подход с комплексным числом имеет такую ​​гибкость. – jlhoward

+0

Ах, хорошая точка. Вращения в 3-пространстве невероятно сложны. Возможно, вам понадобится модель для моделирования физики истребителя. Интересно, есть ли 3D (возможно, не в R) на одном из других форумов stackexchange? –

8

Это еще один метод с использованием комплексных чисел. Вы можете вращать вектор «вправо» в комплексной плоскости, умножая на -1i.Приведенный ниже код делает первый обход идти в положительном Х (Re() - ал ось) и каждый последующий обход будет повернут к «правому»

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)) 

enter image description here

Это подход к с помощью комплексной плоскости вращения, чтобы сделать 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)) 

И сюжет:

enter image description here