Проверьте, совпадают ли два индикатора

Мне дают большую таблицу данных с двумя индикаторами ind1 и ind2 с возможными повторениями. Например.

 set.seed(1)
 ind1 <- sample(1:3,1000, replace=TRUE )
 ind2 <- c("a","b","c")[ind1]

 dt <- data.table(ind1=ind1, ind2=ind2)

Теперь я хотел бы проверить, группируют ли эти два индикатора данные одинаково, т.е.

две строки имеют одинаковый индикатор ind1 тогда и только тогда, когда они также имеют одинаковый индикатор ind2. В приведенном выше примере это будет иметь место по построению.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
0
105
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Вы можете просто сгруппировать по ind2 и подсчитать отдельные ind1 или наоборот. Если какой-либо счетчик > 1, они не группируют данные одинаково. Вот способ с базой R -

any(with(dt, ave(ind1, ind2, FUN = function(x) length(unique(x)))) > 1)

[1] FALSE # means ind1 and ind2 group the data in same way

Кроме того, вы можете проверить, все ли count == 1, используя all, если это легче интерпретировать -

all(with(dt, ave(ind1, ind2, FUN = function(x) length(unique(x)))) == 1)

[1] TRUE # means ind1 and ind2 group the data in same way

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

Это добавит два групповых индекса в таблицу, а также проверит равенство, но вы можете удалить столбцы после, если это необходимо.

dt[,  g1 := .GRP, ind1][, g2 := .GRP, ind2][, all(g1 == g2)]
#[1] TRUE

Редактировать: идея Shree с уникальным подсчетом лучше. См. ниже реализацию data.table.

Edit2: также см. комментарии для других решений.

dt[, uniqueN(ind2), ind1][, all(V1 == 1)]
#[1] TRUE

Сравнительный анализ с таблицей, содержащей 1e7 строк и 10 групп, представленных двумя эквивалентными столбцами.

set.seed(1)
ind1 <- sample(1:10,1e7, replace=TRUE )
ind2 <- c("a","b","c")[ind1]

dt <- data.table(ind1=ind1, ind2=ind2)

microbenchmark::microbenchmark(
grp = dt[,  g1 := .GRP, ind1][, g2 := .GRP, ind2][, all(g1 == g2)], 
uniques = dt[, uniqueN(ind2), ind1][, all(V1 == 1)]
)

# Unit: milliseconds
#     expr      min       lq    mean   median       uq       max neval cld
#      grp 727.9489 838.2190 918.280 879.1036 971.3982 1542.9655   100   b
#  uniques 472.1311 502.1327 529.581 526.5357 540.5406  723.5078   100  a 

Другой: unique(dt[, c("ind1", "ind2")])[, !(anyDuplicated(ind1) || anyDuplicated(ind2))]... и еще один вариант Шри, я думаю: with(dt, max(tapply(ind1, ind2, function(x) length(unique(x))))) == 1L

Frank 16.07.2019 18:35

К вашему сведению, я считаю, что уникальность — это очень плохо, если я масштабирую проблему: чат.stackoverflow.com/transcript/message/46781006#46781006

Frank 16.07.2019 18:50

Для меня, если я изменяю 1e4 на 1e6 в этом связанном тесте, он меняется так, что вариант «uniques» лучше, чем все, кроме «другого». Я бы сказал, что стоит опубликовать эту информацию и другие подходы в качестве отдельного ответа.

IceCreamToucan 16.07.2019 18:56

Хорошо, да, я настраиваю параметры n и ng с момента моего предыдущего комментария и вижу, что сравнения идут во всех направлениях. Опубликую еще один ответ, спасибо

Frank 16.07.2019 18:59
unique(length(ind2)) примерно в два раза быстрее, чем uniqueN(ind2).
Cole 18.07.2019 00:58

Мой инстинкт состоял бы в том, чтобы использовать .GRP, как в ответе @IceCreamToucan, но другой способ - совместно удалить дубликаты для двух столбцов, а затем проверить наличие дубликатов в каждом столбце отдельно:

# data.table
unique(dt[, c("ind1", "ind2")])[, !(anyDuplicated(ind1) || anyDuplicated(ind2))]

# base, with df = data.frame(dt)
with(unique(df[, c("ind1", "ind2")]), !(anyDuplicated(ind1) || anyDuplicated(ind2)))

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

Пример с параметрами для #rows и #groups:

library(data.table)
library(magrittr)

ng = 150
n = 1e6
set.seed(1)
ind1 <- sample(1:ng, n, replace=TRUE )
ind2 <- -ind1

dt <- data.table(ind1=ind1, ind2=ind2)
df = data.frame(dt)

microbenchmark::microbenchmark(times = 3L,
grp = dt[,  g1 := .GRP, ind1][, g2 := .GRP, ind2][, all(g1 == g2)], 
uniques = dt[, uniqueN(ind2), ind1][, all(V1 == 1)],
shreet = with(dt, max(tapply(ind1, ind2, function(x) length(unique(x))))) == 1L,
shreep = with(dt, tapply(ind1, ind2, . %>% unique %>% length)) %>% max %>% equals(1L),
another = unique(dt[, c("ind1", "ind2")])[, !(anyDuplicated(ind1) || anyDuplicated(ind2))],
banother = with(unique(df[, c("ind1", "ind2")]), !(anyDuplicated(ind1) || anyDuplicated(ind2)))
)

Результаты:

Unit: milliseconds
     expr        min         lq       mean     median         uq        max neval
      grp   31.89250   34.92348   46.06510   37.95446   53.15140   68.34833     3
  uniques   32.82520   34.36808   36.32377   35.91097   38.07306   40.23515     3
   shreet   38.26046   38.35256   44.37116   38.44467   47.42650   56.40834     3
   shreep   43.37336   98.56367  145.38600  153.75399  196.39231  239.03064     3
  another   14.47064   31.42879   88.20134   48.38694  125.06669  201.74643     3
 banother 1338.14070 1427.35481 1658.08404 1516.56893 1818.05572 2119.54251     3

я постоянно получаю another, чтобы быть быстрее, независимо от использования microbenchmark или bench::mark. Использование Win 7 R-3.5.1 x64 data.table 1.12.2, DTthreads=4, 16GB RAM

chinsoon12 17.07.2019 02:37

Я тоже получаю another как самый быстрый. Если uniques использует unique(length(ind1)), для меня это второе место со значительным отрывом. У меня 2 ядра/4 потока.

Cole 18.07.2019 01:00

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