Почему мой left.join не работает? Проблема с индексацией?

Мне нужно применить функцию оптимизации к миллионам строк данных, которая возвращает число на основе двух входных переменных. Применение функции с помощью mapply занимает вечность. Поскольку в данных имеется ограниченный диапазон значений с большим количеством повторов, я решил, что будет быстрее создать таблицу поиска для всего диапазона возможных входных комбинаций, а затем просто использовать соединение из dplyr для заполнения данных. Мои попытки не увенчались успехом из-за какого-то странного поведения в моей таблице поиска (на самом деле это фрейм данных). Ниже приведен повторяемый пример. Я пытаюсь понять, почему я не могу ссылаться на некоторые значения в таблице поиска, но другие работают нормально.

## build a lookup table with three columns
lookup <- transform(expand.grid(0:180, seq(0, 35, by=0.1)), v=0)
colnames(lookup) <- c("d", "s", "v")
lookup$v <- 1:nrow(lookup)

## generate some fake data with random values
set.seed(42)  ## for sake of reproducibility
data <- data.frame(d=sample(lookup$d, 1000, replace=TRUE), 
                   s=round(runif (n=1000, min=0, max=35), 1))

## left join gives a lot of NAs 
joined <- dplyr::left_join(x=data, y=lookup, by=c('d', 's'))
joined[1:20, ]
#      d    s     v
# 1   53  5.3    NA
# 2  124 20.2    NA
# 3  172 21.5 39088
# 4  137  5.3    NA
# 5   52 18.8 34081
# 6   67 20.0 36268
# 7   87 21.1 38279
# 8   64 29.4    NA
# 9   97 18.5 33583
# 10 159  8.6 15726
# 11  40 10.4 18865
# 12 138 29.6 53715
# 13  33 15.7    NA
# 14 171 24.4    NA
# 15 126 33.2 60219
# 16 120 20.1 36502
# 17  15 17.3 31329
# 18 104 17.4    NA
# 19  34  3.3    NA
# 20  92 33.6 60909
## unable to reference certain values...
lookup$v[which(lookup$d == 177 & lookup$s == 8.2)]
#integer(0)

## ...even though they exist
lookup[15020, ]
#      d   s   v
#15020 177 8.2 15020

## other values are OK
lookup$v[which(lookup$d == 177 & lookup$s == 8.3)]
#[1] 15201

Может быть, вы могли бы умножить на десять, чтобы работать только с целыми числами?

crestor 16.06.2024 23:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы имеете дело с точностью с плавающей запятой и, следовательно, не получаете того, чего ожидаете. Начнем с вашего второго вопроса.

Вместо

> lookup[15020, ]
        d   s     v
15020 177 8.2 15020
>
> lookup$s[15020] == 8.2
[1] FALSE

пытаться

> tol <- .Machine$double.eps^0.5
> lookup$s[15020] - 8.2 < tol
[1] TRUE
> lookup$v[which(lookup$d == 177 & abs(lookup$s - 8.2) < tol)]
[1] 15020

Подробнее об этом читайте в этом ответе.

Это также может относиться к вашему первому вопросу о том, что у merge есть проблемы.

Вместо этого, используя merge (или dplyr::*join), мы могли бы расширить логику tolerance.

> f <- \(x, y, tol=.Machine$double.eps^0.5) {
+   which(
+     colSums(
+       abs(t(lookup[, c('d', 's')]) - c(x, y)) < rep_len(tol, 2),
+     ) == 2L
+   )
+ }
> res1 <- data |> transform(v=lookup$v[mapply(f, data$d, data$s)])
> res1 |> head(20) 
     d    s     v
1   53  5.3  9647
2  124 20.2 36687
3  172 21.5 39088
4  137  5.3  9731
5   52 18.8 34081
6   67 20.0 36268
7   87 21.1 38279
8   64 29.4 53279
9   97 18.5 33583
10 159  8.6 15726
11  40 10.4 18865
12 138 29.6 53715
13  33 15.7 28451
14 171 24.4 44336
15 126 33.2 60219
16 120 20.1 36502
17  15 17.3 31329
18 104 17.4 31599
19  34  3.3  6008
20  92 33.6 60909

Поскольку это медленно, мы могли бы использовать Rcpp.

> Rcpp::cppFunction('
+ IntegerVector match_tol(NumericMatrix lookup, NumericMatrix data) {
+   int n = data.nrow();
+   IntegerVector res(n);
+   double tol = sqrt(std::numeric_limits<double>::epsilon());
+   for (int i = 0; i < n; ++i) {
+     double d = data(i, 0);  // first data column
+     double s = data(i, 1);  // second
+     for (int j = 0; j < lookup.nrow(); ++j) {
+       if (lookup(j, 0) == d && std::abs(lookup(j, 1) - s) < tol) {
+         res[i] = lookup(j, 2);
+         break;
+       }
+     }
+   }
+   return res;
+ }
+ ')

> res2 <- data |> transform(v=match_tol(as.matrix(lookup), as.matrix(data)))
> res2 |> head(20)
     d    s     v
1   53  5.3  9647
2  124 20.2 36687
3  172 21.5 39088
4  137  5.3  9731
5   52 18.8 34081
6   67 20.0 36268
7   87 21.1 38279
8   64 29.4 53279
9   97 18.5 33583
10 159  8.6 15726
11  40 10.4 18865
12 138 29.6 53715
13  33 15.7 28451
14 171 24.4 44336
15 126 33.2 60219
16 120 20.1 36502
17  15 17.3 31329
18 104 17.4 31599
19  34  3.3  6008
20  92 33.6 60909

Другие решения, в том числе base::merge(., sort=FALSE) в числовом или символьном формате, могут по-прежнему иметь проблемы*:

> joined1 <- merge(data, lookup, all.x=TRUE, sort=FALSE) 
> joined1 |> head(20)
     d    s     v
1   53  5.3  9647
2  124 20.2 36687
3  172 21.5 39088
4  137  5.3  9731
5   52 18.8 34081
6   67 20.0 36268
7   87 21.1 38279
8   64 29.4 53279
9   97 18.5 33583
10 159  8.6 15726
11  40 10.4 18865
12 138 29.6 53715
13  33 15.7 28451
14 171 24.4 44336
15 126 33.2 60219
16 120 20.1 36502
17  15 17.3 31329
18 104 17.4 31599  ## *
19 104 17.4 31599  ## *
20  34  3.3  6008

> joined2 <- merge(
+   sapply(data, as.character), 
+   sapply(lookup, as.character), 
+   all.x=TRUE, sort=FALSE) |> 
+   sapply(as.numeric) |> 
+   as.data.frame()  ## optional
> joined2 |> head(20)
     d    s     v
1   53  5.3  9647
2  124 20.2 36687
3  172 21.5 39088
4  137  5.3  9731
5   52 18.8 34081
6   67 20.0 36268
7   87 21.1 38279
8   64 29.4 53279
9   97 18.5 33583
10 159  8.6 15726
11  40 10.4 18865
12 138 29.6 53715
13  33 15.7 28451
14 171 24.4 44336
15 126 33.2 60219
16 120 20.1 36502
17  15 17.3 31329
18 104 17.4 31599  ## *
19 104 17.4 31599  ## *
20  34  3.3  6008

Сравнивать:

> data |> head(20)
     d    s
1   53  5.3
2  124 20.2
3  172 21.5
4  137  5.3
5   52 18.8
6   67 20.0
7   87 21.1
8   64 29.4
9   97 18.5
10 159  8.6
11  40 10.4
12 138 29.6
13  33 15.7
14 171 24.4
15 126 33.2
16 120 20.1
17  15 17.3
18 104 17.4
19  34  3.3
20  92 33.6

Данные:

> lookup <- expand.grid(d=0:180, s=seq(0, 35, by=0.1)) |>
+   transform(v=seq_len(35/.1 + 1))
> set.seed(42)
> data <- data.frame(d=sample(lookup$d, 1000, replace=TRUE), 
+                    s=round(runif (n=1000, min=0, max=35), 1))

Спасибо @jay.sf. К сожалению, у слияния та же проблема: ~~~ merged <- base::merge(x=data, y=lookup, all.x=T, sort =F) merged[1:20,] # d s v # 1 179 4.6 NA # 2 45 12,9 23395 # 3 162 28,1 51024 # 4 25 11,2 NA # 5 65 14,4 26130 # 6 64 32,5 58890 # 7 95 9,3 16929 # 8 70 31,6 57267 # 9 92 2 6,4 НП № 10 129 12,0 21850 № 11 7 11,0 19918 # 12 59 34,6 62686 # 13 3 8,1 14665 # 14 146 3,0 5577 # 15 16 26,8 48525 # 16 15 27,5 49791 # 17 136 6,2 11359 # 18 70 6,5 11836 # 19 103 0,4 828 # 20 99 22,6 41006 ~~~

birdy448 16.06.2024 17:02

@birdy448 Вы использовали all.x=TRUE,, поэтому включены data, которых нет в lookup.

jay.sf 16.06.2024 17:06

Блин, это форматирование комментариев просто чудовище. Ничего не работает...

birdy448 16.06.2024 17:08

Я думал, что это может быть как-то связано с точностью с плавающей запятой, но я не знал о настройках допуска. Может стоит попробовать. Также можно попробовать заменить цифры на символы.

birdy448 16.06.2024 17:10

@birdy448 Вы можете добавить информацию в виде редактирования к своему вопросу.

jay.sf 16.06.2024 17:10

@birdy448 Использование персонажа — НЕ очень хорошая идея. Пожалуйста, смотрите редактирование.

jay.sf 16.06.2024 17:46

Итак, подход слияния, похоже, теперь работает. По крайней мере, я не получаю НС. Повторяющиеся значения, которые вы получаете (@jay.sf), вполне ожидаемы, хотя при 'sort = FALSE' странно (для меня), что они постоянно оказываются рядом друг с другом. Я добавил последовательность 1 к nrow() к данным, чтобы отслеживать исходный порядок. Затем я применяю слияние и сортировку фрейма данных, чтобы восстановить исходный порядок строк.

birdy448 16.06.2024 18:00

@birdy448 Отлично, какое из трех решений вы в итоге использовали?

jay.sf 16.06.2024 18:11

@birdy448 Birdy448 См. решение Rcpp в редактировании.

jay.sf 16.06.2024 18:55

Другие вопросы по теме