Предположим, у меня есть два кадра данных.Первый кадр данных - это известный набор данных 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к средствам, где мы сопоставляем атрибуты ближайшей записи?
Очевидный ответ:
- Перекрестное соединение двух фреймов данных.
- Групповой ранг,где группа - это запись радужной оболочки.Ранг будет в порядке возрастания абсолютной разницы между фактическими значениями и средними значениями для каждого вида.
- Выберите высший ранг для каждой группы.
Примерно так:
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
- это прогнозируемый столбец, основанный на расстоянии Манхэттена до среднего значения ближайшего вида.
Есть ли лучший способ сделать это?Перекрестное соединение подходит для небольшого набора данных, но в масштабе выглядит как анти-шаблон.