Самый быстрый способ проверить уникальные значения и вернуть его, если в R data.table есть только одно уникальное значение - PullRequest
9 голосов
/ 02 мая 2020

Предположим, у меня большой data.table, который выглядит как dt ниже.

dt <- data.table(
  player_1 = c("a", "b", "b", "c"),
  player_1_age = c(10, 20, 20, 30),
  player_2 = c("b", "a", "c", "a"),
  player_2_age = c(20, 10, 30, 10)
)
# dt
#    player_1 player_1_age player_2 player_2_age
# 1:        a           10        b           20
# 2:        b           20        a           10
# 3:        b           20        c           30
# 4:        c           30        a           10

Из dt выше, я хотел бы создать data.table с уникальными игроками и их возрастом. как показано ниже: player_dt:

# player_dt
# player  age
#      a   10
#      b   20
#      c   30

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

Как бы вы получили player_dt выше, при проверке для каждого player, что существует только одно уникальное age значение ?

# get unique players
player <- sort(unique(c(dt$player_1, dt$player_2)))

# for each player, get their age, if there is only one age value
age <- sapply(player, function(x) {
  unique_values <- unique(c(
    dt[player_1 == x][["player_1_age"]],
    dt[player_2 == x][["player_2_age"]]))
  if(length(unique_values) > 1) stop() else return(unique_values)
})

# combine to create the player_dt
player_dt <- data.table(player, age)

Ответы [ 2 ]

7 голосов
/ 02 мая 2020

Я использую данные из @DavidT в качестве входных данных.

dt
#   player_1 player_1_age player_2 player_2_age
#1:        a           10        b           20
#2:        b           20        a           10
#3:        b           20        c           30
#4:        c           30        a           11 # <--

TL; DR

Вы можете сделать

nm <- names(dt)
idx <- endsWith(nm, "age")
colsAge <- nm[idx]
colsOther <- nm[!idx]

out <-
  unique(melt(
    dt,
    measure.vars = list(colsAge, colsOther),
    value.name = c("age", "player")
  )[, .(age, player)])[, if (.N == 1) # credit: https://stackoverflow.com/a/34427944/8583393
    .SD, by = player]
out
#   player age
#1:      b  20
#2:      c  30

Шаг за шагом

Что вы можете сделать, это растопить несколько столбцов одновременно - те, которые заканчиваются на "age", и те, которые не заканчиваются.

nm <- names(dt)
idx <- endsWith(nm, "age")
colsAge <- nm[idx]
colsOther <- nm[!idx]
dt1 <- melt(dt, measure.vars = list(colsAge, colsOther), value.name = c("age", "player"))

Результат

dt1
#   variable age player
#1:        1  10      a
#2:        1  20      b
#3:        1  20      b
#4:        1  30      c
#5:        2  20      b
#6:        2  10      a
#7:        2  30      c
#8:        2  11      a

Теперь мы вызываем unique ...

out <- unique(dt1[, .(age, player)])
out
#   age player
#1:  10      a
#2:  20      b
#3:  30      c
#4:  11      a

... и фильтруем для групп "player" с длиной, равной 1

out <- out[, if(.N == 1) .SD, by=player]
out
#   player age
#1:      b  20
#2:      c  30

Учитывая входные данные OP, последний шаг не требуется.

data

library(data.table)
dt <- data.table(
  player_1 = c("a", "b", "b", "c"),
  player_1_age = c(10, 20, 20, 30),
  player_2 = c("b", "a", "c", "a"),
  player_2_age = c(20, 10, 30, 11)
)

Ссылка: https://cran.r-project.org/web/packages/data.table/vignettes/datatable-reshape.html

2 голосов
/ 02 мая 2020

Я изменил ваши данные таким образом, что нужно поймать хотя бы одну ошибку:

library(tidyverse)

dt <- tibble(
  player_1 = c("a", "b", "b", "c"),
  player_1_age = c(10, 20, 20, 30),
  player_2 = c("b", "a", "c", "a"),
  player_2_age = c(20, 10, 30, 11)
)
  # Get the Names columns and the Age columns
colName <- names(dt)
ageCol <- colName[str_detect(colName, "age$")]
playrCol <- colName[! str_detect(colName, "age$")]

  # Gather the Ages
ages <- dt %>% 
  select(ageCol) %>% 
  gather(player_age, age)

  # Gather the names
names <- dt %>% 
  select(playrCol ) %>% 
  gather(player_name, name)

  # Bind the two together, and throw out the duplicates
  # If there are no contradictions, this is what you want.
allNameAge <- cbind( names, ages) %>% 
  select(name, age) %>% 
  distinct() %>% 
  arrange(name)

  # But check for inconsistencies.  This should leave you with
  # an empty tibble, but instead it shows the error.
inconsistencies <- allNameAge %>% 
  group_by(name) %>% 
  mutate(AGE.COUNT = n_distinct(age)) %>% 
  filter(AGE.COUNT > 1) %>% 
  ungroup()

Это должно распространяться на большее количество пар столбцов имя / возраст.

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