Skip to content

Commit 4e88ccd

Browse files
Added function for putting points in sequence for path plotting, see ?point.in.sequence
1 parent 2393fa3 commit 4e88ccd

File tree

5 files changed

+173
-0
lines changed

5 files changed

+173
-0
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,4 @@ Collate:
7979
'TRL-line.R'
8080
'multiplot.R'
8181
'ggsave.R'
82+
'point-in-sequence.R'

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,5 @@ export(theme_arrowlength)
8686
export(weight_percent)
8787
export(atomic_percent)
8888
export(custom_percent)
89+
export(point.in.sequence)
8990
export(ggsave)

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
ggtern 1.0.3.2
22
----------------------------------------------------------------
33
* Added convenience functions, and global options for modifying the length of the ternary arrows easily.
4+
* Added function for putting points in sequence for path plotting, see ?point.in.sequence
45

56
BUG FIXES
67
* Fixed broken ggsave function, which was not using the local print function.

R/point-in-sequence.R

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#' Put Points in Sequence
2+
#'
3+
#' The \code{point.in.sequence} function takes numeric input vectors \code{x} and \code{y} or a
4+
#' \code{\link{data.frame}} object, and orders the values in such way that they are correctly sequenced by the angle subtended between each point,
5+
#' and, the centroid of the total set. If the data is provided in the format of a \code{data.frame}, then it must
6+
#' containing columns named \code{x} and \code{y}, else an error will be thrown.
7+
#'
8+
#' The arguments \code{x} and \code{y} represent cartesian coordinates. This is useful if a path is sought that
9+
#' passes through each point in the ordered set, however, no two lines in the total path cross over each other.
10+
#' Uses the \code{\link{atan2}} function to determine the angle (theta) between each point (x,y) and the centroid
11+
#' of the data, it then orders based on increasing values of theta.
12+
#'
13+
#' @param x vector of numeric \code{x} values
14+
#' @param y vector of numeric \code{y} values
15+
#' @param ... not used
16+
#' @param df data.frame containing colums \code{x} and \code{y}
17+
#' @param close logical value (default \code{FALSE}), as to whether the set should be closed by adding (duplicating)
18+
#' the first row (after ordering) to the end of the set.
19+
#' @return \code{data.frame} object containing the re-ordered input set.
20+
#' @examples
21+
#' \donttest{
22+
#' #Load plotting library
23+
#' library(ggplot2)
24+
#'
25+
#' #For reproducability
26+
#' set.seed(1)
27+
#'
28+
#' #Build data in an approximate loop
29+
#' theta <- seq(0,2*pi,by=pi/100)
30+
#' r <- 1 + (runif(length(theta))-0.5)/5
31+
#' df2 <- data.frame(x=r*cos(theta),y=r*sin(theta))
32+
#'
33+
#' #Randomise the order of the data
34+
#' df2 <- df2[sample(nrow(df2)),]
35+
#'
36+
#' #Function to plot data
37+
#' demo <- function(ret){
38+
#' ggplot(data=ret,aes(x,y)) +
39+
#' geom_path() +
40+
#' geom_point(fill="yellow",color="black",shape=21)
41+
#' }
42+
#'
43+
#' #Demonstrate how the data would plot WITHOUT sorting
44+
#' demo(df2)
45+
#'
46+
#' #Demonstrate how the data would plot WITH sorting
47+
#' demo(point.in.sequence(df=df2,close=TRUE))
48+
#' }
49+
#' @export
50+
point.in.sequence <- function(x,y,...,df=data.frame(x=x,y=y),close=FALSE){
51+
#If first argument is provided as data.frame, re-assign to df
52+
if(!missing(x))
53+
if(class(x) == "data.frame")
54+
df = x
55+
56+
#Check df is dataframe
57+
if(class(df) != "data.frame")
58+
stop("df must be a data.frame",call.=F)
59+
60+
#Check 2 or more unique rows exist.
61+
if(nrow(unique(df)) <= 1)
62+
stop("df must contain at least two unique rows",call.=FALSE)
63+
64+
#Check correct columns exist
65+
sapply(c("x","y"),function(X){
66+
if(!X %in% colnames(df))
67+
stop(paste("df must contain column",X),call.=F)
68+
if(!is.numeric(df[,X]))
69+
stop(paste("df must contain column",X,"and must be numeric"),call.=F)
70+
})
71+
72+
#Check close argument is logical
73+
close = ifthenelse(is.logical(close),close[1],FALSE)
74+
75+
#Center point
76+
c.x = mean(df$x)
77+
c.y = mean(df$y)
78+
79+
#Determine angle with center point
80+
df$theta <- apply(df[,c("x","y")],1,function(r) atan2(r[2] - c.y,r[1] - c.x)*180/pi)
81+
82+
#Put in correct order and
83+
df <- df[with(df,base::order(theta)), ]
84+
85+
#Make a closed loop if desired
86+
#by duplicating the first row on the bottom
87+
if(close)
88+
df <- rbind(df,df[1,])
89+
90+
#Remove theta column
91+
df$theta=NULL
92+
93+
#And return
94+
df
95+
}

man/point.in.sequence.Rd

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
\name{point.in.sequence}
2+
\alias{point.in.sequence}
3+
\title{Put Points in Sequence}
4+
\usage{
5+
point.in.sequence(x, y, ...,
6+
df = data.frame(x = x, y = y), close = FALSE)
7+
}
8+
\arguments{
9+
\item{x}{vector of numeric \code{x} values}
10+
11+
\item{y}{vector of numeric \code{y} values}
12+
13+
\item{...}{not used}
14+
15+
\item{df}{data.frame containing colums \code{x} and
16+
\code{y}}
17+
18+
\item{close}{logical value (default \code{FALSE}), as to
19+
whether the set should be closed by adding (duplicating)
20+
the first row (after ordering) to the end of the set.}
21+
}
22+
\value{
23+
\code{data.frame} object containing the re-ordered input
24+
set.
25+
}
26+
\description{
27+
The \code{point.in.sequence} function takes numeric input
28+
vectors \code{x} and \code{y} or a
29+
\code{\link{data.frame}} object, and orders the values in
30+
such way that they are correctly sequenced by the angle
31+
subtended between each point, and, the centroid of the
32+
total set. If the data is provided in the format of a
33+
\code{data.frame}, then it must containing columns named
34+
\code{x} and \code{y}, else an error will be thrown.
35+
}
36+
\details{
37+
The arguments \code{x} and \code{y} represent cartesian
38+
coordinates. This is useful if a path is sought that
39+
passes through each point in the ordered set, however, no
40+
two lines in the total path cross over each other. Uses
41+
the \code{\link{atan2}} function to determine the angle
42+
(theta) between each point (x,y) and the centroid of the
43+
data, it then orders based on increasing values of theta.
44+
}
45+
\examples{
46+
\donttest{
47+
#Load plotting library
48+
library(ggplot2)
49+
50+
#For reproducability
51+
set.seed(1)
52+
53+
#Build data in an approximate loop
54+
theta <- seq(0,2*pi,by=pi/100)
55+
r <- 1 + (runif(length(theta))-0.5)/5
56+
df2 <- data.frame(x=r*cos(theta),y=r*sin(theta))
57+
58+
#Randomise the order of the data
59+
df2 <- df2[sample(nrow(df2)),]
60+
61+
#Function to plot data
62+
demo <- function(ret){
63+
ggplot(data=ret,aes(x,y)) +
64+
geom_path() +
65+
geom_point(fill="yellow",color="black",shape=21)
66+
}
67+
68+
#Demonstrate how the data would plot WITHOUT sorting
69+
demo(df2)
70+
71+
#Demonstrate how the data would plot WITH sorting
72+
demo(point.in.sequence(df=df2,close=TRUE))
73+
}
74+
}
75+

0 commit comments

Comments
 (0)