Search code examples
rgeometrytrigonometrycomputational-geometry

How to calculate a list of angles between two line vectors in a series of specimens in R?


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?


Solution

  • 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.frames. 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