Есть ли функция R, эквивалентная команде Stata 'order'?

«порядок» в R выглядит как «сортировка» в Stata. Вот, например, набор данных (перечислены только имена переменных):

v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18

и вот результат, который я ожидаю:

v1 v2 v3 v4 v5 v7 v8 v9 v10 v11 v12 v17 v18 v13 v14 v15 v6 v16

В R у меня есть 2 способа:

data <- data[,c(1:5,7:12,17:18,13:15,6,16)]

OR

names <- c("v1", "v2", "v3", "v4", "v5", "v7", "v8", "v9", "v10", "v11", "v12",  "v17", "v18", "v13", "v14", "v15", "v6", "v16")
data <- data[names]

Чтобы получить такой же результат в Stata, я могу запустить 2 строки:

order v17 v18, before(v13)
order v6 v16, last

В идеальных данных выше мы можем знать позиции переменных, с которыми мы хотим иметь дело. Но в большинстве реальных случаев у нас есть такие переменные, как «возраст», «пол», без индикаторов положения, и у нас может быть более 50 переменных в одном наборе данных. Тогда преимущество «порядка» в Stata могло быть более очевидным. Нам не нужно знать точное место переменной, просто введите ее имя:

order age, after(gender)

Есть ли в R базовая функция для решения этой проблемы, или я могу получить пакет? Заранее спасибо.

tweetinfo <- data.frame(uid=1:50, mid=2:51, annotations=3:52, bmiddle_pic=4:53, created_at=5:54, favorited=6:55, geo=7:56, in_reply_to_screen_name=8:57, in_reply_to_status_id=9:58, in_reply_to_user_id=10:59, original_pic=11:60, reTweetId=12:61, reUserId=13:62, source=14:63, thumbnail_pic=15:64, truncated=16:65)
noretweetinfo <- data.frame(uid=21:50, mid=22:51, annotations=23:52, bmiddle_pic=24:53, created_at=25:54, favorited=26:55, geo=27:56, in_reply_to_screen_name=28:57, in_reply_to_status_id=29:58, in_reply_to_user_id=30:59, original_pic=31:60, reTweetId=32:61, reUserId=33:62, source=34:63, thumbnail_pic=35:64, truncated=36:65)
retweetinfo <- data.frame(uid=41:50, mid=42:51, annotations=43:52, bmiddle_pic=44:53, created_at=45:54, deleted=46:55, favorited=47:56, geo=48:57, in_reply_to_screen_name=49:58, in_reply_to_status_id=50:59, in_reply_to_user_id=51:60, original_pic=52:61, source=53:62, thumbnail_pic=54:63, truncated=55:64)
tweetinfo$type <- "ti"
noretweetinfo$type <- "nr"
retweetinfo$type <- "rt"
gtinfo <- rbind(tweetinfo, noretweetinfo)
gtinfo$deleted=""
gtinfo <- gtinfo[,c(1:16,18,17)]
retweetinfo <- transform(retweetinfo, reTweetId="", reUserId="")
retweetinfo <- retweetinfo[,c(1:5,7:12,17:18,13:15,6,16)]
gtinfo <- rbind(gtinfo, retweetinfo)
write.table(gtinfo, file="C:/gtinfo.txt", row.names=F, col.names=T, sep="\t", quote=F)
# rm(list=ls(all=T))

person leoce    schedule 22.09.2012    source источник
comment
Почему вы хотите заказать колонны? Обычно не заботится о порядке столбцов (переменных) в data.frame, а только о порядке строк (наблюдений).   -  person Roland    schedule 22.09.2012
comment
... и даже порядок в строках часто бывает излишним, за исключением случаев, когда наблюдения имеют четкий порядок, например, в временных рядах.   -  person Paul Hiemstra    schedule 22.09.2012
comment
У меня есть 3 набора данных, 2 из которых не включают v6, а другой не включает v17 и v18. Я хочу создать v16, чтобы записать источники данных и объединить их вместе. Я создал недостающие переменные с нулевыми значениями в каждой, и я хочу экспортировать вывод rbind () в текстовый файл с тем же порядком переменных с набором данных 1 и 2, добавив в конце v6 и v16 (источник).   -  person leoce    schedule 22.09.2012
comment
Задайте это как вопрос с помощью воспроизводимого кода. Это можно сделать гораздо лучше.   -  person Roland    schedule 22.09.2012
comment
@Roland Я поместил код внизу, чтобы смоделировать мою ситуацию.   -  person leoce    schedule 22.09.2012
comment
Пожалуйста, прочтите ?rbind. Если аргументы для rbind - data.frames, столбцы сопоставляются по имени, а не по позиции. Заказывать их не нужно.   -  person Roland    schedule 22.09.2012
comment
продолжение комментария @ Roland: это означает (я думаю), что команда retweetinfo <- retweetinfo[,c(1:5,7:12,17:18,13:15,6,16)] совершенно не нужна ...   -  person Ben Bolker    schedule 22.09.2012
comment
Но я хочу экспортировать текстовый файл в точном порядке, он все еще не нужен?   -  person leoce    schedule 22.09.2012


Ответы (6)


Поскольку я откладываю на потом и экспериментирую с разными вещами, вот функция, которую я придумал. В конечном итоге это зависит от append:

moveme <- function(invec, movecommand) {
  movecommand <- lapply(strsplit(strsplit(movecommand, ";")[[1]], ",|\\s+"), 
                        function(x) x[x != ""])
  movelist <- lapply(movecommand, function(x) {
    Where <- x[which(x %in% c("before", "after", "first", "last")):length(x)]
    ToMove <- setdiff(x, Where)
    list(ToMove, Where)
  })
  myVec <- invec
  for (i in seq_along(movelist)) {
    temp <- setdiff(myVec, movelist[[i]][[1]])
    A <- movelist[[i]][[2]][1]
    if (A %in% c("before", "after")) {
      ba <- movelist[[i]][[2]][2]
      if (A == "before") {
        after <- match(ba, temp)-1
      } else if (A == "after") {
        after <- match(ba, temp)
      }    
    } else if (A == "first") {
      after <- 0
    } else if (A == "last") {
      after <- length(myVec)
    }
    myVec <- append(temp, values = movelist[[i]][[1]], after = after)
  }
  myVec
}

Вот несколько примеров данных, представляющих имена вашего набора данных:

x <- paste0("v", 1:18)

Теперь представьте, что нам нужны «v17» и «v18» перед «v3», «v6» и «v16» в конце и «v5» в начале:

moveme(x, "v17, v18 before v3; v6, v16 last; v5 first")
#  [1] "v5"  "v1"  "v2"  "v17" "v18" "v3"  "v4"  "v7"  "v8"  "v9"  "v10" "v11" "v12"
# [14] "v13" "v14" "v15" "v6"  "v16"

Итак, очевидное использование для data.frame с именем "df":

df[moveme(names(df), "how you want to move the columns")]

И для data.table с именем "DT" (что, как указывает @mnel, будет более эффективно использовать память):

setcolorder(DT, moveme(names(DT), "how you want to move the columns"))

Обратите внимание, что составные ходы обозначаются точкой с запятой.

Признанные ходы:

  • before (переместить указанные столбцы до другого именованного столбца)
  • after (переместить указанные столбцы после другого именованного столбца)
  • first (переместить указанные столбцы в первую позицию)
  • last (переместить указанные столбцы в последнюю позицию)
person A5C1D2H2I1M1N2O1R2T1    schedule 24.08.2013

Я понимаю твою проблему. Теперь у меня есть код:

move <- function(data,variable,before) {
  m <- data[variable]
  r <- data[names(data)!=variable]
  i <- match(before,names(data))
  pre <- r[1:i-1]
  post <- r[i:length(names(r))]
  cbind(pre,m,post)
}

# Example.
library(MASS)
data(painters)
str(painters)

# Move 'Expression' variable before 'Drawing' variable.
new <- move(painters,"Expression","Drawing")
View(new)
person Fr.    schedule 22.09.2012
comment
Это очень новаторский способ мышления - разделить данные на 3 части. Прямо сейчас он может не относиться к перемещению нескольких переменных, но мы можем пойти дальше в этом направлении. Большое тебе спасибо. - person leoce; 23.09.2012
comment
Имейте в виду, что этот подход неэффективен, и его следует избегать для больших наборов данных или внутри циклов. - person Roland; 23.09.2012
comment
@Roland Простой принцип упорядочивания переменных неэффективен, но я обнаружил, что это похоже на имена переменных, что иногда нужно исправить. - person Fr.; 25.09.2012
comment
@leoce Вы можете сделать параметр variable функции вектором переменных, если это то, что вам нужно: измените переменную r на data[!(names(data) %in% variable)]. - person Fr.; 25.09.2012
comment
@Fr. Нет, я имел в виду, что ваша функция неэффективна. В частности, разделение data.frames и cbinding являются неэффективными операциями, которых здесь можно было бы избежать. - person Roland; 25.09.2012

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

Следующее даст вам новый порядок имен столбцов с использованием синтаксиса, аналогичного stata.

  • where - именованный список с 4 вариантами

    • list(last = T)
    • list(first = T)
    • list(before = x), где x - это имя переменной, о которой идет речь
    • list(after = x), где x - это имя переменной, о которой идет речь
  • sorted = T отсортирует var_list лексикографически (комбинация alphabetic и sequential из команды stata

Функция работает только с именами (как только вы передаете объект data.frame как data и возвращает переупорядоченный список имен

eg

stata.order <- function(var_list, where, sorted = F, data) {
    all_names = names(data)
    # are all the variable names in
    check <- var_list %in% all_names
    if (any(!check)) {
        stop("Not all variables in var_list exist within  data")
    }
    if (names(where) == "before") {
        if (!(where %in% all_names)) {
            stop("before variable not in the data set")
        }
    }
    if (names(where) == "after") {
        if (!(where %in% all_names)) {
            stop("after variable not in the data set")
        }
    }

    if (sorted) {
        var_list <- sort(var_list)
    }
    where_in <- which(all_names %in% var_list)
    full_list <- seq_along(data)
    others <- full_list[-c(where_in)]

    .nwhere <- names(where)
    if (!(.nwhere %in% c("last", "first", "before", "after"))) {
        stop("where must be a list of a named element first, last, before or after")
    }

    do_what <- switch(names(where), last = length(others), first = 0, before = which(all_names[others] == 
        where) - 1, after = which(all_names[others] == where))

    new_order <- append(others, where_in, do_what)
    return(all_names[new_order])
}

tmp <- as.data.frame(matrix(1:100, ncol = 10))

stata.order(var_list = c("V2", "V5"), where = list(last = T), data = tmp)

##  [1] "V1"  "V3"  "V4"  "V6"  "V7"  "V8"  "V9"  "V10" "V2"  "V5" 

stata.order(var_list = c("V2", "V5"), where = list(first = T), data = tmp)

##  [1] "V2"  "V5"  "V1"  "V3"  "V4"  "V6"  "V7"  "V8"  "V9"  "V10"

stata.order(var_list = c("V2", "V5"), where = list(before = "V6"), data = tmp)

##  [1] "V1"  "V3"  "V4"  "V2"  "V5"  "V6"  "V7"  "V8"  "V9"  "V10"

stata.order(var_list = c("V2", "V5"), where = list(after = "V4"), data = tmp)

##  [1] "V1"  "V3"  "V4"  "V2"  "V5"  "V6"  "V7"  "V8"  "V9"  "V10"

# throws an error
stata.order(var_list = c("V2", "V5"), where = list(before = "v11"), data = tmp)

## Error: before variable not in the data set

если вы хотите эффективно переупорядочить память (по ссылке, без копирования), используйте data.table

DT <- data.table(tmp)
# sets by reference, no copying
setcolorder(DT, stata.order(var_list = c("V2", "V5"), where = list(after = "V4"), 
    data = DT))

DT

##     V1 V3 V4 V2 V5 V6 V7 V8 V9 V10
##  1:  1 21 31 11 41 51 61 71 81  91
##  2:  2 22 32 12 42 52 62 72 82  92
##  3:  3 23 33 13 43 53 63 73 83  93
##  4:  4 24 34 14 44 54 64 74 84  94
##  5:  5 25 35 15 45 55 65 75 85  95
##  6:  6 26 36 16 46 56 66 76 86  96
##  7:  7 27 37 17 47 57 67 77 87  97
##  8:  8 28 38 18 48 58 68 78 88  98
##  9:  9 29 39 19 49 59 69 79 89  99
## 10: 10 30 40 20 50 60 70 80 90 100
person mnel    schedule 02.10.2012

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

Собственно, есть встроенная функция order, которая возвращает индексы упорядоченной последовательности. Вы ищите это?

> x <- c(3,2,1)

> order(x)
[1] 3 2 1

> x[order(x)]
[1] 1 2 3
person rlegendi    schedule 22.09.2012
comment
Это наименьшее, что я хочу сделать - отсортировать данные. Порядок в Stata означает другое, что тот, кто его использовал, может понять. - person leoce; 22.09.2012

Это должно дать вам тот же файл:

#snip
gtinfo <- rbind(tweetinfo, noretweetinfo)
gtinfo$deleted=""
retweetinfo <- transform(retweetinfo, reTweetId="", reUserId="")
gtinfo <- rbind(gtinfo, retweetinfo)
gtinfo <-gtinfo[,c(1:16,18,17)]
#snip

В R можно реализовать такую ​​функцию, как Strata order, но я не думаю, что в этом есть большой спрос.

person Roland    schedule 23.09.2012
comment
Эм, это не большая проблема для всех, и люди, которым это интересно, могут разобраться в этом. - person leoce; 23.09.2012
comment
@leoce Я хочу сказать, что вас это интересовало только потому, что вы еще не знакомы с R и пришли из Stata. В своем ответе я показал, что вам не нужно загромождать свой код порядком. Фактически, вам нужно сделать заказ только один раз, и это только потому, что вам нужен определенный порядок в вашем выходном файле. - person Roland; 23.09.2012
comment
вы правы, gtinfo ‹-gtinfo [, c (1: 16,18,17)] наконец-то лучше, чем то, что я сделал с двумя строками вроде c (1: 5,7: 12,17: 18,13: 15,6,16). Но вы не можете отрицать, что в R нет такой базовой функции для регулировки порядка столбцов. Я не могу связать его и сказать своему боссу. Видите ли, программа заказывает это автоматически, и вам лучше к этому привыкнуть. - person leoce; 23.09.2012
comment
Я не понимаю. Вы можете заказать его, используя базовые функции, как показано выше. Если вы не хотите работать с индексами, вы также можете использовать имена столбцов, возможно, используя subset. - person Roland; 23.09.2012
comment
data ‹- данные [, c (A, C, B)] OR data‹ - data [, c (1,3,2)] OR data ‹- subset (data, select = c (1,3,2)) , каждый из них действительно мог работать. Но что, если я получу 50 или более столбцов? Мне нужно ввести все имена столбцов или вручную найти номер столбца объекта и пункта назначения. - person leoce; 23.09.2012
comment
Если вам нужно заказать 50 или более столбцов, вы делаете что-то очень странное. Но вы можете определить любую функцию, которая, по вашему мнению, вам нужна. :) - person Roland; 23.09.2012
comment
... У меня 50 столбцов, и я хочу переместить "возраст" "пол" после "родной город". Перед данными ‹- data [, c (, *:, **)] у меня есть 3 строки для запуска: which (colnames (data) == age) // which (colnames ( data) == пол) // который (colnames (data) == родной город). Я не вижу здесь эффективности R. - person leoce; 23.09.2012

Пакет dplyr и функция dplyr::relocate, новый глагол, представленный в dplyr 1.0.0, делают именно то, что вы ищете.

library(dplyr)

data %>% relocate(v17, v18, .before = v13)

data %>% relocate(v6, v16, .after = last_col())

data %>% relocate(age, .after = gender)

person Arthur Yip    schedule 20.04.2020