R: Узнайте, сколько точек лежит на прямой линии на группу - PullRequest
2 голосов
/ 06 июня 2019

Я пытаюсь найти решение моей проблемы:

сколько точек на группу лежит на прямой линии

Я не смог найти решение этой проблемы в R ...

Ниже У вас есть пример данных и график, чтобы показать вам, как это выглядит:

data <- structure(list(Group = c(22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 22782L, 
22782L, 11553L, 11553L, 11553L, 11553L, 11553L, 7059L, 7059L, 
7059L, 7059L, 22782L), x = c(100L, 150L, 250L, 287L, 312L, 387L, 
475L, 550L, 837L, 937L, 987L, 1087L, 1175L, 1300L, 1325L, 1487L, 
1662L, 1700L, 1725L, 1812L, 1912L, 2412L, 3012L, 3562L, 4162L, 
4762L, 5362L, 5750L, 5712L, 6225L, 6825L, 6887L, 7237L, 7850L, 
7800L, 7937L, 7975L, 8275L, 8362L, 8662L, 8725L, 8950L, 9100L, 
9312L, 9400L, 9600L, 4637L, 900L, 4187L, 5800L, 7075L, 1125L, 
3400L, 3562L, 3462L, 5412L), y = c(493L, 482L, 479L, 476L, 481L, 
479L, 474L, 480L, 480L, 491L, 489L, 490L, 485L, 485L, 485L, 479L, 
482L, 482L, 482L, 482L, 484L, 489L, 491L, 489L, 496L, 498L, 500L, 
0L, 498L, 500L, 502L, 506L, 497L, 0L, 495L, 506L, 497L, 494L, 
498L, 500L, 496L, 499L, 496L, 495L, 495L, 498L, 825L, 284L, 850L, 
360L, 790L, 861L, 883L, 882L, 881L, 502L)), row.names = c(23L, 
24L, 25L, 26L, 27L, 28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L, 
37L, 38L, 39L, 40L, 41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 
51L, 52L, 53L, 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 
64L, 65L, 66L, 67L, 68L, 69L, 281L, 312L, 313L, 315L, 316L, 377L, 
378L, 380L, 511L, 815L), class = "data.frame")

Данные состоят из столбца имени группы (в данном случае 3 группы), координат x и y:

 Group   x   y
22782 100 493
22782 150 482
22782 250 479
22782 287 476
22782 312 481

Ниже мы можем найти сюжет группы 22782: enter image description here

Как видите, есть много точек, которые лежат почти точно на одной линии, и я хотел бы выяснить, сколько из них на группу соответствует этому условию.

Ожидаемый результат будет выглядеть следующим образом:

  Group Max Points  
  22782  20

Буду признателен за любую помощь или советы! Спасибо

Ответы [ 3 ]

1 голос
/ 06 июня 2019

Давайте предположим, что вы знаете , только меньшинство точек не на линии.Вы также упоминаете, что хотите рассматривать только горизонтальные линии.

В этом случае вы можете использовать median в качестве надежной оценки положения горизонтальной линии.Вы можете использовать mean, но он может колебаться от экстремальных значений, которые в любом случае отсутствуют в строке.

Код self_explanatory:

tolerance <- 10

data %>%
  group_by(Group) %>%
  mutate(y_line = median(y), 
         on_line = abs(y - y_line) <= tolerance) %>%
  count(Group, on_line)

Результат:

#   Group on_line     n
#   <int> <lgl>   <int>
# 1  7059 FALSE       1
# 2  7059 TRUE        3
# 3 11553 FALSE       4
# 4 11553 TRUE        1
# 5 22782 FALSE      13
# 6 22782 TRUE       34

Конечно, вы можете передать это в filter(on_line), чтобы сохранить только количество точек на линии.

1 голос
/ 06 июня 2019

Мне кажется, что это проблема оптимизации интервала (или, в более общем смысле, кластеризация одномерных данных), то есть, если у вас нет фиксированных разрывов или линий, я могу подумать, что для решения такой проблемы есть Оптимизация естественных разрывов Дженкса , которая уже реализована в R в пакете BAMMtools

Сначала вы исправляете линии, а затем видите, какие точки принадлежат какой линии (ближайшая линия)

В параметре getJenksBreaks.

можно указать число строк (или, скорее, кластеров), которое может быть задано, но могут быть и другие методы для кластеризации этих точек, но здесь есть jenks

library(BAMMtools)
lines <- getJenksBreaks(mydata$y, 5)
lines
# [1]   0   0 360 506 883
mydata <- mydata %>% 
  rowwise() %>% 
  mutate(line_id = as.character(which.min(abs(y-unique(lines))))) 

mydata %>% 
  group_by(Group, line_id) %>% 
  summarise(cnt =n()) %>% 
  group_by(Group) %>% 
  summarise(max_points = max(cnt))
# 
# # A tibble: 3 x 2
#   Group max_points
#   <int>      <dbl>
# 1  7059          4
# 2 11553          3
# 3 22782         45

mydata %>% 
  #filter(Group == 22782) %>% 
  ggplot(aes(x,y, color = line_id)) + 
  geom_point() +
  geom_hline(yintercept = lines, 
             color = 'red', 
             #alpha = 0.5, 
             linetype ='dashed', 
             size = 0.3) +
  facet_grid(.~Group)

enter image description here

1 голос
/ 06 июня 2019

Поскольку мы не знаем, какие значения имеют строки в ggplot, нам нужно выяснить, какие разрывы установлены по умолчанию.Ответ на этот вопрос здесь и используется в моем коде.

Следующая функция сообщает, сколько точек на линиях в группе.Далее вы можете установить значение tolerance, какие отклонения от линии вы принимаете.Кроме того, иногда точки могут лежать на разных линиях, как в случае ggplot(subset(data, Group == 22782), aes(x=x,y=y)) + geom_point(), где точки лежат на двух разных линиях (0 и 500).

plot

В этом случае вы можете решить, хотите ли вы знать сумму всех точек, находящихся на любой линии, или если вас интересует наибольшее количество точек, набираемых на одной линии (здесь указано количество точек на 500).Вы можете выбрать это с помощью any_or_max_line.

Функция

points.on.lines <- function(data, tolerance, any_or_max_line){
# runs the code below per group
sapply(unique(data$Group), function(group_i){
  # chooses i-th group
  data_group_i <- subset(data, Group == group_i)
# find on which y-values the lines are
line_values <- 
  with(data_group_i,
       labeling::extended(range(y)[1], range(y)[2], m = 5))
# find out per line how many points are on or around that line
points_on_lines <- sapply(line_values, function(line_values_i){
  sum(data_group_i$y >= line_values_i - tolerance &
        data_group_i$y <= line_values_i + tolerance)})
# decides whether to take into account the line with most points or all points on any line
if(any_or_max_line == "max"){
  points_on_lines <- max(points_on_lines)
} else {
  points_on_lines <- sum(points_on_lines)
}
# names results by group
names(points_on_lines) <- paste0("Group_", group_i)
return(points_on_lines)
})}

Пример

points.on.lines(data= data, tolerance= 50,
                any_or_max_line= "max")
Group_22782 Group_11553  Group_7059 
     45           3           4 
...