# Automatic selection of nuber or factors in PCA based on the Cattells scree plot in R

I would like to automatically select the number of factors after factor analysis (PCA). I mean the graphical selection method according to the Cattel criterion, which determines where the steepness of the graph ends:

Therefore, I want to check where the first steepness of the graph ends, withouth looking on plot. For any set of data (df) I have at my disposal percentages of explained variance, raw values square roots of the eigenvalues, and so on:

# df is my data set
pca_results <- prcomp(df)
# here i can get plot values
plot_values <- pca_results$$sdev # ... and change plot values to percents of variance explained variance_percents <- (pca_results$$sdev^2 / sum(pca_results\$sdev^2)) * 100
# i can take values higher than 1 (Kaiser's simple criterion)
pca_number_of_factors <- length(plot_values[plot_values > 1])
# get some variance percents
first_f_var_perc <- variance_percents[1]
second_f_var_perc <- variance_percents[2]
third_f_var_perc <- variance_percents[3]
# or mark steppness level
PCA_steepness = 1 - second_f_var_perc / first_f_var_perc


I'm looking for a simple mathematical way to determine the first steepness collapse of a graph. Something like "number of factors" selected automatically in this way. Could someone help me in this regard?

Of course, I am aware of the disadvantages of this approach (for example, a few slopes of the graph), but I would just like to find out how it can be solved geometrically-numerically, because frankly, I don't have much in mind.

OK, to be honest i don't know if i'm not missing something from simple trigonomethry, but here's my solution.

From the standardized set of plot_values obtained in the previous code, you can create a simple plot and look at it:

plot(plot_values)
plot(plot_values, type="l")


What visually makes a decrease in the steepness of the graph? That a line is at an angle of less than 45 degrees to the horizontal axis. Here we got first line with more than 45 degrees.

Given that the graph/plot points are standardized, the distances between the individual objects are the same (they are 1, that is line a) then the steepness ends where the direct value relative to the next point (line c) is simply less than the y-axis 1. Then the line b will be directed at an angle less than 45 degrees and its steepness will end. So we can find it like this:

# make new variable
plot_values2 = plot_values
# let's leave the heights of the triangles the same,
# that is, let's subtract the next value from the previous one
for (i in 1:(length(plot_values2)-1))
{ plot_values2[i] = plot_values2[i] - plot_values2[i+1] }
# with last value = 0 not to distort our results
plot_values2[length(plot_values2)] = 0
# let's get first result lower than 1, and that's it
number_of_PCA_factors = (match(TRUE, (plot_values2 < 1))) - 1
# and for any problems:
if (number_of_PCA_factors < 1) {number_of_PCA_factors = NA}


If there is something wrong with this set of code, please let me know or correct it.