I am doing work a geometric morphometric dataset of biological specimens with two dimensional Cartesian coordinates (i.e., landmarks). Part of my interest with this dataset is to produce a data frame of distances between important landmarks and angles between landmark vectors with homologous coordinates across all specimens for further comparison. Distances between points have not been a problem, but I have been running into problems when I have tried to calculate angles in each of my specimens.
I am trying to find a way to calculate the angle between line vectors representing the same angle on a series of specimens at once (the vectors describe a feature with biological significance that is present in all specimens across my dataset). The R package I use to manipulate the morphometric data outputs the landmarks as a three-dimensional object of class array()
where rows are landmarks, columns are the dimensions of the coordinate, and the third dimension is the specimen. Here are the landmark coordinates for the three specimens in the sample data presented here.
, , Specimen1
X Y
1 -0.2411 0.060183
2 -0.0677 -0.029954
3 0.1147 0.012111
4 0.0085 0.000462
, , Specimen2
X Y
1 -0.22509 0.0764
2 -0.09437 -0.0202
3 0.09135 -0.0182
4 0.00367 -0.0045
, , Specimen3
X Y
1 -0.2223 0.06122
2 -0.1001 -0.02366
3 0.0553 0.00577
4 -0.0617 -0.01557
Below is a readable array that replicates the data I get as output for the first three specimens.
data<-array(data=c(-0.2411,-0.0677,0.1147,0.0085,0.060183,-0.029954,0.012111,0.000462,-0.22509,-0.09437,0.09135,0.00367,0.0764,-0.0202,-0.0182,-0.0045,-0.2223,-0.1001,0.0553,-0.0617,0.06122,-0.02366,0.00577,-0.01557),c(4,2,3))
I am interested in finding the angle formed by the lines between points 1 and 2 and points 3 and 4. To do this I calculate the vector of these two lines by subtracting the coordinates of one point from the other. When I do that I get a 2*N matrix for each vector where rows correspond to vectors in the X and Y axis and columns correspond to specimen.
I want to find the angle between these two vectors for specimen 1 (column 1 on both matrices), 2, 3, and so on. Ideally, I would like to get the output into a format where R reports the angle for each indivdual specimen, like so:
Angle1
[,1] 146
[,2] 152
[,3] 135
And then somehow add it into a data frame so it looks like this:
Distance Angle1 Angle2
Specimen1 100 146 100
Specimen2 100 152 100
Specimen3 100 135 100
The distances and values for Angle2 are just filler numbers to show the format I am trying to achieve.
I have tried using the angle()
function in the mathlib package and the angle.calc()
function in the Morpho package. Here is the code I used to calculate the angle using the angle()
function.
vector1<-data[1,,]-data[2,,]
vector2<-data[3,,]-data[4,,]
angle(as.vector(vector1),as.vector(vector2),degree=TRUE)
These functions will calculate what seems to be the accurate angle however they will only do it for the first specimen or a single specimen if otherwise specified, and do not produce a list of angles for all specimens. I have also checked previous questions on Stack Overflow but none of the answers given appear to be applicable to a dataset containing multiple specimens with each column on both matrices representing a distinct specimen (and when I apply them at best they seem to have the same problem where they only return the angle for the first specimen). There are only three specimens here, I have a large number of specimens and multiple angles per specimen in the actual dataset and it is not possible to measure every angle individually for each specimen.
Is there any way to write a formula to produce a list containing the respective angle for each of these specimens?
First off, it's a bit confusing to give a 2x3
matrix the name vector1
. That aside, you can use matlib::angle
to calculate angles.
library(matlib)
phi <- setNames(mapply(
function(x1, x2) angle(x1, x2),
as.data.frame(vector1), as.data.frame(vector2)),
paste0("Specimen", 1:ncol(vector1)))
phi
#Specimen1 Specimen2 Specimen3
# 146.2674 152.4439 134.8753
Explanation: Use mapply
to simultaneously iterate over the columns of vector1
and vector2
, which have been converted to data.frame
s. Use stack
to convert the named vector into a data.frame
:
stack(phi)
# values ind
#1 146.2674 Specimen1
#2 152.4439 Specimen2
#3 134.8753 Specimen3