объединить два кадра данных в ближайшую запись - PullRequest
0 голосов
/ 01 февраля 2019

Предположим, у меня есть два кадра данных.Первый кадр данных - это известный набор данных iris:

> data(iris)
> print(iris)
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
2            4.9         3.0          1.4         0.2     setosa
3            4.7         3.2          1.3         0.2     setosa
4            4.6         3.1          1.5         0.2     setosa
5            5.0         3.6          1.4         0.2     setosa
.... (150 observations)

Второй набор данных содержит средства для каждого атрибута:

library(dplyr)
iris.means <- iris %>%
  group_by(Species) %>%
  summarize(Sepal.Length = mean(Sepal.Length),
            Sepal.Width = mean(Sepal.Width),
            Petal.Length = mean(Petal.Length),
            Petal.Width = mean(Petal.Width))

> print(iris.means)
# A tibble: 3 x 5
  Species    Sepal.Length Sepal.Width Petal.Length Petal.Width
  <fct>             <dbl>       <dbl>        <dbl>       <dbl>
1 setosa             5.01        3.43         1.46       0.246
2 versicolor         5.94        2.77         4.26       1.33 
3 virginica          6.59        2.97         5.55       2.03 

Существует ли элегантный способ (слева) присоединиться к кадру данных irisк средствам, где мы сопоставляем атрибуты ближайшей записи?

Очевидный ответ:

  1. Перекрестное соединение двух фреймов данных.
  2. Групповой ранг,где группа - это запись радужной оболочки.Ранг будет в порядке возрастания абсолютной разницы между фактическими значениями и средними значениями для каждого вида.
  3. Выберите высший ранг для каждой группы.

Примерно так:

iris <- iris %>% mutate(id = row_number())

iris.means <- iris %>%
  group_by(Species) %>%
  summarize(Sepal.Length.Mean = mean(Sepal.Length),
            Sepal.Width.Mean = mean(Sepal.Width),
            Petal.Length.Mean = mean(Petal.Length),
            Petal.Width.Mean = mean(Petal.Width))

names(iris.means)[names(iris.means) == "Species"] <- "Species.Mean"

iris.crossjoin <- merge(iris, iris.means, all=TRUE)

В наборе перекрестных данных есть три записи для каждой из исходных записей радужной оболочки.Мы начали с 150 записей;теперь у нас есть 450:

> arrange(iris.crossjoin, id)
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species id Species.Mean Sepal.Length.Mean Sepal.Width.Mean Petal.Length.Mean Petal.Width.Mean
1           5.1         3.5          1.4         0.2  setosa  1       setosa             5.006            3.428             1.462            0.246
2           5.1         3.5          1.4         0.2  setosa  1   versicolor             5.936            2.770             4.260            1.326
3           5.1         3.5          1.4         0.2  setosa  1    virginica             6.588            2.974             5.552            2.026
4           4.9         3.0          1.4         0.2  setosa  2       setosa             5.006            3.428             1.462            0.246
5           4.9         3.0          1.4         0.2  setosa  2   versicolor             5.936            2.770             4.260            1.326
6           4.9         3.0          1.4         0.2  setosa  2    virginica             6.588            2.974             5.552            2.026
.... (450 observations)

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

iris.crossjoin <- iris.crossjoin %>%
  mutate(Sepal.Length.Delta = abs(Sepal.Length.Mean - Sepal.Length),
         Sepal.Width.Delta = abs(Sepal.Width.Mean - Sepal.Width),
         Petal.Length.Delta = abs(Petal.Length.Mean - Petal.Length),
         Petal.Width.Delta = abs(Petal.Width.Mean - Petal.Width),
         Delta.Sum = Sepal.Length.Delta + Sepal.Width.Delta + Petal.Length.Delta + Petal.Width.Delta)

Затем мы можем отфильтровать записи из объединенных наборов данных, которые имеют самое близкое расстояние к среднему:

iris.crossjoin <- iris.crossjoin %>% arrange(id, Delta.Sum) %>%
  group_by(id) %>% 
  mutate(rank = rank(Delta.Sum, ties.method = "first"),
         correct = Species == Species.Mean) %>%
  filter(rank == 1) 

Вывод будет выглядеть примерно так:

> iris.crossjoin[c('id', 'Sepal.Length', 'Sepal.Width', 'Petal.Length', 'Petal.Width', 'Species', 'Species.Mean', 'correct')]

     id Sepal.Length Sepal.Width Petal.Length Petal.Width    Species Species.Mean correct
1     1          5.1         3.5          1.4         0.2     setosa       setosa    TRUE
2     2          4.9         3.0          1.4         0.2     setosa       setosa    TRUE
...
52   52          6.4         3.2          4.5         1.5 versicolor   versicolor    TRUE
53   53          6.9         3.1          4.9         1.5 versicolor    virginica   FALSE
54   54          5.5         2.3          4.0         1.3 versicolor   versicolor    TRUE
...

Столбец Species - это фактическое значение, а Species.Mean - это прогнозируемый столбец, основанный на расстоянии Манхэттена до среднего значения ближайшего вида.

Есть ли лучший способ сделать это?Перекрестное соединение подходит для небольшого набора данных, но в масштабе выглядит как анти-шаблон.

...