Проверка, отсортированы ли строки матрицы или фрейма данных в R

#r #sorting #matlab

#r #сортировка #matlab

Вопрос:

Каков эффективный способ проверить, отсортированы ли строки в матрице? [Обновление: см. Ответ Aaron’s Rcpp — простой и очень быстрый.]

Я переношу некоторый код, который использует issorted(,'rows') Matlab. Поскольку кажется, что is.unsorted это не выходит за рамки векторов, я пишу или ищу что-то еще. Наивный метод заключается в проверке того, что отсортированная версия матрицы (или фрейма данных) совпадает с исходной, но это, очевидно, неэффективно.

ПРИМЕЧАНИЕ: для сортировки a la sortrows() в Matlab мой код по существу использует SortedDF <- DF[do.call(order, DF),] (он заключен в более крупную функцию, которая преобразует матрицы в фреймы данных, передает параметры order и т. Д.). Я не удивлюсь, если появятся более быстрые реализации (на ум приходит таблица данных).


Обновление 1: Чтобы уточнить: я не тестирую сортировку внутри строки или внутри столбцов. (Такая сортировка обычно приводит к алгебраически отличной матрице.)

В качестве примера для создания несортированной матрицы:

 set.seed(0)
x <- as.data.frame(matrix(sample(3, 60, replace = TRUE), ncol = 6, byrow = TRUE))
  

Его отсортированная версия:

 y <- x[do.call(order, x),]
  

Скажем, правильный тест testSorted вернет FALSE for testSorted(x) и TRUE for testSorted(y) .

Обновление 2: Все приведенные ниже ответы хороши — они краткие и выполняют тест. Что касается эффективности, похоже, что они все-таки сортируют данные.

Я пробовал их с довольно большими матрицами, такими как 1 М x 10, (просто изменив x приведенное выше создание), и все они имеют примерно одинаковую стоимость времени и памяти. Особенность в том, что все они занимают больше времени для несортированных объектов (около 5,5 секунд для 1Mx10), чем для отсортированных (около 0,5 секунды для y ). Это предполагает, что они сортируются перед тестированием.

Я проверил, создав z матрицу:

 z <- y
z[,2] <- y[,1]
z[,1] <- y[,2]
  

В этом случае выполнение всех методов занимает около 0,85 секунды. В любом случае, завершение за 5,5 секунд не страшно (на самом деле, это, похоже, соответствует времени, необходимому для сортировки объекта), но знание того, что отсортированная матрица в 11 раз быстрее, предполагает, что тест, который не сортирует, может быть еще быстрее. В случае матрицы из 1 строки первые три строки x :

   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1  3  1  2  2  3  1  3  3  2   2
2  1  1  1  3  2  3  2  3  3   2
3  3  3  1  2  1  1  2  1  2   3
  

Нет необходимости смотреть дальше строки 2, хотя векторизация — неплохая идея.

(Я также добавил byrow аргумент для создания x , чтобы значения строк не зависели от размера x .)

Обновление 3: Другое сравнение для этого тестирования можно найти с sort -c помощью команды в Linux. Если файл уже записан (используется write.table() ) с 1 МЛН строк, то time sort -c myfile.txt для несортированных данных требуется 0,003 секунды, а для отсортированных данных — 0,101 секунды. Я не собираюсь записывать в файл, но это полезное сравнение.

Обновление 4: Метод Rcpp Аарона превзошел все другие методы, предложенные здесь, и которые я пробовал (включая sort -c сравнение выше: ожидается, что in-memory превзойдет on-disk). Что касается соотношения относительно других методов, трудно сказать: знаменатель слишком мал, чтобы дать точное измерение, и я не изучал microbenchmark его подробно. Ускорения могут быть очень большими (на 4-5 порядков) для некоторых матриц (например, для матрицы, созданной с rnorm помощью ), но это вводит в заблуждение — проверка может завершиться только через пару строк. У меня были ускорения с примерами матриц примерно на 25-60 для несортированных и примерно в 1,1 раза для отсортированных, поскольку конкурирующие методы были уже очень быстрыми, если данные отсортированы.

Поскольку это делает правильные вещи (т. Е. Без сортировки, просто тестирование) и делает это очень быстро, это принятый ответ.

Ответ №1:

Если y отсортирован, то do.call(order,y) возвращает 1:nrow(y) .

  testSorted = function(y){all(do.call(order,y)==1:nrow(y))}
  

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

Комментарии:

1. Теперь я понимаю, что вы имеете в виду, говоря о том, что они не выпадают, когда есть несоответствие. Итак, это краткая, но все же сортировка. Мне нужно будет больше подумать об этом.

Ответ №2:

Ну, почему бы вам не использовать:

 all(do.call(order, y)==seq(nrow(y)))
  

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

Комментарии:

1. 1 Это многообещающе. Я упустил seq из виду проблему с нечисловыми значениями во фрейме данных, с которыми order работает.

Ответ №3:

Новее: я решил, что могу использовать практику Rcpp…

 library(Rcpp)
library(inline)
isRowSorted <- cxxfunction(signature(A="numeric"), body='
  Rcpp::NumericMatrix Am(A);
  for(int i = 1; i < Am.nrow(); i  ) {
    for(int j = 0; j < Am.ncol(); j  ) {
      if( Am(i-1,j) < Am(i,j) ) { break; }
      if( Am(i-1,j) > Am(i,j) ) { return(wrap(false)); }
    }
  }
  return(wrap(true));
', plugin="Rcpp")

rownames(y) <- NULL # because as.matrix is faster without rownames
isRowSorted(as.matrix(y))
  

Новое: этот взлом только для R имеет одинаковую скорость для всех матриц; это определенно быстрее для отсортированных матриц; для несортированных это зависит от характера несортированности.

 iss3 <- function(x) {
  x2 <- sign(do.call(cbind, lapply(x, diff)))
  x3 <- t(x2)*(2^((ncol(x)-1):0))
  all(colSums(x3)>=0)
}
  

Оригинал: это быстрее для некоторых несортированных матриц. Насколько быстрее это будет зависеть от того, где находятся несортированные элементы; это просматривает матрицу по столбцам, поэтому несортированность с левой стороны будет замечена намного быстрее, чем несортированная справа, в то время как верх / низ не имеет большого значения.

 iss2 <- function(y) {
  b <- c(0,nrow(y))
  for(i in 1:ncol(y)) {
    z <- rle(y[,i])
    b2 <- cumsum(z$lengths)
    sp <- split(z$values, cut(b2, breaks=b))
    for(spi in sp) {
      if(is.unsorted(spi)) return(FALSE)
    }
    b <- c(0, b2)
  }
  return(TRUE)
}
  

Комментарии:

1. @Iterator: насколько это замедлит работу, зависит от характера матрицы; он работает с результатами rle , поэтому, если есть несколько категорий (как в вашем примере, где их три), это кажется довольно быстрым; если категорий много, я ожидаю, чтоэто значительно замедлило бы работу.

2. N больше, если бы я мог. Это здорово. Это очень быстро, и трюк с rownames умным. Я ожидал попробовать Rcpp, но я бы не догадался об rownames ускорении. Одно предложение для других, которые используют это: isS4 (заглавные буквы S) — это базовая команда R. Будьте осторожны.

3. Выглядит хорошо! Я бы использовал a bool out вместо a LogicalVector(1) , а затем использовал return(wrap(out)); для возврата.

4. Добавление: если объект уже является матрицей (т. Е. Выполняет rownames и as.matrix вне времени), Выполнение выполняется очень быстро. Возможно, мне нужно учиться microbenchmark . 🙂

5. Теперь использует wrap и был переименован в isRowSorted .

Ответ №4:

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

Этот подход может быть легко реализован и протестирован на R, а затем перенесен на простую функцию C , которую мы можем подключить к R через inline и Rcpp (или обычный C, если необходимо), поскольку цикл — это то, что действительно выгодно для реализации на скомпилированном языке.

В противном случае, вы не можете использовать что-то вроде diff() и проверить, все ли приращения неотрицательны?

Комментарии:

1. 1, но я рекомендую вам написать это на обычном C, используя vim, в Windows. 😉

2. Нет, diff() не совсем сработает. 🙂 Попробуйте это на примере выше: diff(as.matrix(y)) . Появляются некоторые отрицательные значения; итак, что можно сделать, так это убедиться, что положительные значения всегда предшествуют отрицательным значениям для каждой разностной строки. Я подозреваю, что это все еще неэлегантно.

3. Джош, настоящие программисты на C не используют ничего, кроме редактора строк, такого как ed.

4. Ну, или вы делаете умные вещи в R напрямую, как показали Барри и Ник.

5. После того, как дальнейшее тестирование показало, что альтернативы включают сортировку, кажется, что может потребоваться итерация, и в этом случае Rcpp кажется многообещающим.

Ответ №5:

Вы можете использовать свой do.call оператор с is.unsorted :

 issorted.matrix <- function(A) {!is.unsorted(do.call("order",data.frame(A)))}