Значение подстановки из нескольких столбцов, которое наиболее близко соответствует другому столбцу

Я пытаюсь найти наиболее близкое соответствие значению из lastseen из ряда других столбцов при посещении сайта (d1, d2, d3, d4, d5), чтобы создать новый столбец nextvisit со значением из d1 , d2, d3, d4 или d5, который является следующим по величине значением в lastseen (т.е. следующее посещение после того, как человека видели в последний раз).

Воспроизводимый пример:

indiv lastseen d1  d2  d3  d4   d5
A     2         2   4   5   8   10
B     5         2   3   5   7    9
C     9         1   6   9  11   15

Итак, ответ, который я ищу:

indiv lastseen d1  d2  d3  d4   d5  nextvisit
A     2         2   4   5   8   10          4
B     5         2   3   5   7    9          7
C     9         1   6   9  11   15         11

Например, 4 - это следующее по величине число в столбцах d1:d5 над 2 для индивидуума A.

Я пробовал использовать tidyr и dplyr, но не могу эффективно найти следующее по величине совпадение.

Спасибо

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
401
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

Используя tidyverse, мы gather столбцы от 'd1' до 'd5' в 'длинный' формат, сгруппированные по 'indiv', создаем столбец разницы между 'val' и 'last seen', slice строку, которая имеет минимальное положительное значение. , select интересующие столбцы и выполнить соединение с исходным набором данных

library(tidyverse)
df1 %>% 
   gather(key, val, d1:d5) %>%
   group_by(indiv) %>% 
   mutate(Diff = val -lastseen, 
          Diff = replace(Diff, Diff <=0, NA)) %>% 
   slice(which.min(Diff)) %>% 
   select(indiv, val) %>% 
   right_join(df1) %>%
   select(names(df1), everything())
# A tibble: 3 x 8
# Groups:   indiv [3]
#  indiv lastseen    d1    d2    d3    d4    d5   val
#  <chr>    <int> <int> <int> <int> <int> <int> <int>
#1 A            2     2     4     5     8    10     4
#2 B            5     2     3     5     7     9     7
#3 C            9     1     6     9    11    15    11

Другой вариант - использовать max.col от base R. Получите разницу столбцов 'd' с столбцами 'последнего посещения' в объекте ('m1'), замените значения, которые меньше или равны 0, на очень большое число, используйте max.col, чтобы получить индекс столбца каждая строка, имеющая максимальное значение (обратная логика - изменила его на отрицательное), cbind с индексом строки и извлекает значение из столбцов 'd', которые ему соответствуют.

m1 <- df1[3:7] -df1$lastseen
m1[m1 <=0] <- 999
df1$val <- df1[3:7][cbind(seq_len(nrow(df1)), max.col(-m1, 'first'))]
df1$val
#[1]  4  7 11

данные

df1 <- structure(list(indiv = c("A", "B", "C"), lastseen = c(2L, 5L, 
9L), d1 = c(2L, 2L, 1L), d2 = c(4L, 3L, 6L), d3 = c(5L, 5L, 9L
), d4 = c(8L, 7L, 11L), d5 = c(10L, 9L, 15L)), 
class = "data.frame", row.names = c(NA, -3L))

Считайте, что df - это ваш data.frame. Вот подход с полной базой R

> ind <- (df[, -c(1,2)]- df[, 2])>0
> df$nextvisit <- apply(df[, -c(1,2)]*ind, 1, function(x) min(x[x!=0]))
> df
  indiv lastseen d1 d2 d3 d4 d5 nextvisit
1     A        2  2  4  5  8 10         4
2     B        5  2  3  5  7  9         7
3     C        9  1  6  9 11 15        11
df = read.table(text = "
indiv lastseen d1  d2  d3  d4   d5
                A     2         2   4   5   8   10
                B     5         2   3   5   7    9
                C     9         1   6   9  11   15
                ", header=T)

library(tidyverse)

df %>%
  group_by(indiv, lastseen) %>%  # for each combination
  nest() %>%                     # nest data
  mutate(nextvisit = map2(lastseen, data, ~{vec = unlist(.y); min(vec[vec > .x])})) %>%  # get the minimum value higher than the corresponding lastseen value
  unnest()                       # unnest data

# # A tibble: 3 x 8
#   indiv lastseen nextvisit    d1    d2    d3    d4    d5
#   <fct>    <int>     <int> <int> <int> <int> <int> <int>
# 1 A            2         4     2     4     5     8    10
# 2 B            5         7     2     3     5     7     9
# 3 C            9        11     1     6     9    11    15

Хорошее использование map2

akrun 16.08.2018 22:44

Спасибо. Искал функцию "получше", но, похоже, мне нужен unlist.

AntoniosK 16.08.2018 22:59

Другой вариант base R

idx <- which(DF[-c(1, 2)] == DF$lastseen, arr.ind = TRUE)
idx[, "col"] <- idx[, "col"] + 1 # lastseen + 1 = next visit (in terms of column positions)
DF$nextvisit <- DF[-c(1, 2)][idx]
DF
#  indiv lastseen d1 d2 d3 d4 d5 nextvisit
#1     A        2  2  4  5  8 10         4
#2     B        5  2  3  5  7  9         7
#3     C        9  1  6  9 11 15        11

данные

DF <- structure(list(indiv = c("A", "B", "C"), lastseen = c(2L, 5L, 
9L), d1 = c(2L, 2L, 1L), d2 = c(4L, 3L, 6L), d3 = c(5L, 5L, 9L
), d4 = c(8L, 7L, 11L), d5 = c(10L, 9L, 15L)), .Names = c("indiv", 
"lastseen", "d1", "d2", "d3", "d4", "d5"), class = "data.frame", row.names = c(NA, 
-3L))

другой базовый способ R:

df$lastvisit <- apply(df[,-1], 1, function(x) min(tail(x,5)[tail(x,5)>head(x,1)]))

или менее читаемый, но короче:

df$lastvisit <- apply(df[,-1], 1, function(x) min(x[-1][x[-1]>x[1]]))

Вот решение, которое сочетает в себе функции data.table и Find():

library(data.table)
setDT(df1)[, nextvisit := Find(function(x) x > lastseen, .SD), .SDcols = d1:d5, by = indiv]
df1[]
   indiv lastseen d1 d2 d3 d4 d5 nextvisit
1:     A        2  2  4  5  8 10         4
2:     B        5  2  3  5  7  9         7
3:     C        9  1  6  9 11 15        11

Find() применяет функцию фильтрации function(x) x > lastseen к столбцам .SD слева направо в каждой строке и возвращает первый элемент, который удовлетворяет условию. Столбцы .SD определены .SDcols = d1:d5.

Обратите внимание, что значения в каждой строке должны быть уже отсортированы в порядке возрастания слева направо. Если не уверен, .SD. можно заменить на sort(.SD).

Решение tidyverse, полностью векторизованное и без преобразования в матрицу здесь:

library(tidyverse)
df1 %>% 
  transmute_at(vars(starts_with("d")), ~ ifelse(.x>.y, .x, Inf), .$lastseen) %>%
  invoke(pmin,.) %>%
  bind_cols(df1,nextvisit=.)

#      indiv lastseen d1 d2 d3 d4 d5 nextvisit
#    1     A        2  2  4  5  8 10         4
#    2     B        5  2  3  5  7  9         7
#    3     C        9  1  6  9 11 15        11

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