[r] 여러 열로 데이터 프레임을 정렬하는 방법

data.frame을 여러 열로 정렬하고 싶습니다. 예를 들어 아래의 data.frame을 사용하면 zb(오름차순) , 열 (오름차순) 별로 정렬하고 싶습니다 .

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"),
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
dd
    b x y z
1  Hi A 8 1
2 Med D 3 1
3  Hi A 9 1
4 Low C 9 2



답변

order()애드온 툴에 의존하지 않고 함수를 직접 사용할 수 있습니다 . example(order)코드 상단에서 트릭을 사용하는 간단한 답변을 참조하십시오 .

R> dd[with(dd, order(-z, b)), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

약 2 년 후에 편집 : 방금 인덱스로이를 수행하는 방법을 묻습니다. 답은 단순히 원하는 정렬 열을 order()함수에 전달하는 것입니다.

R> dd[order(-dd[,4], dd[,1]), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1
R> 

열 이름을 사용하는 대신 (보다 with()쉽고 직접적인 액세스).


답변

당신의 선택

  • order …에서 base
  • arrange …에서 dplyr
  • setordersetorderv에서data.table
  • arrange …에서 plyr
  • sort …에서 taRifx
  • orderBy …에서 doBy
  • sortData …에서 Deducer

의존성이없는 것이 중요하지 않은 한 대부분의 경우 dplyr또는 data.table솔루션을 사용해야 base::order합니다.


최근에 sort.data.frame을 CRAN 패키지에 추가하여 다음과 같이 클래스를 호환 가능하게 만들었습니다.
sort.data.frame의 일반 / 방법 일관성을 만드는 가장 좋은 방법은 무엇입니까?

따라서 data.frame dd가 주어지면 다음과 같이 정렬 할 수 있습니다.

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"),
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(taRifx)
sort(dd, f= ~ -z + b )

이 기능의 원저자 중 하나 인 경우 저에게 연락하십시오. 공개 도메인에 대한 토론은 여기에 있습니다 : http://chat.stackoverflow.com/transcript/message/1094290#1094290


Hadley가 위의 스레드에서 지적한 것처럼 arrange()함수를 사용할 수도 있습니다 plyr.

library(plyr)
arrange(dd,desc(z),b)

벤치 마크 : 많은 충돌이 있었으므로 새 R 세션에서 각 패키지를로드했습니다. 특히 doBy 패키지를로드하면 sort“다음 객체가 ‘x (위치 17)’에서 마스크됩니다 : b, x, y, z”가 반환되고 Deducer 패키지를로드하면 sort.data.frameKevin Wright 또는 taRifx 패키지에서 덮어 씁니다 .

#Load each time
dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"),
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(microbenchmark)

# Reload R between benchmarks
microbenchmark(dd[with(dd, order(-z, b)), ] ,
    dd[order(-dd$z, dd$b),],
    times=1000
)

평균 시간 :

dd[with(dd, order(-z, b)), ] 778

dd[order(-dd$z, dd$b),] 788

library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)

평균 시간 : 1,567

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)

평균 시간 : 862

library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)

평균 시간 : 1,694

doBy는 패키지를로드하는 데 약간의 시간이 걸립니다.

library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)

Deducer를로드하지 못했습니다. JGR 콘솔이 필요합니다.

esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}

microbenchmark(esort(dd, -z, b),times=1000)

부착 / 분리로 인해 마이크로 벤치 마크와 호환되지 않는 것 같습니다.


m <- microbenchmark(
  arrange(dd,desc(z),b),
  sort(dd, f= ~-z+b ),
  dd[with(dd, order(-z, b)), ] ,
  dd[order(-dd$z, dd$b),],
  times=1000
  )

uq <- function(x) { fivenum(x)[4]}
lq <- function(x) { fivenum(x)[2]}

y_min <- 0 # min(by(m$time,m$expr,lq))
y_max <- max(by(m$time,m$expr,uq)) * 1.05

p <- ggplot(m,aes(x=expr,y=time)) + coord_cartesian(ylim = c( y_min , y_max ))
p + stat_summary(fun.y=median,fun.ymin = lq, fun.ymax = uq, aes(fill=expr))

마이크로 벤치 마크 플롯

(선은 사분 위에서 사분 위로 이어지고 점은 중앙값입니다)


이러한 결과를 감안할 때 단순 비교 속도를 무게, 나는에 고개를 끄덕 제공해야 할 것 arrangeplyr패키지 . 간단한 구문을 가지고 있지만 복잡한 기계 가공을 사용하는 기본 R 명령만큼이나 빠릅니다. 일반적으로 훌륭한 Hadley Wickham이 작업합니다. 그것에 대한 유일한 견해는 정렬 객체가 호출되는 표준 R 명명법을 위반한다는 것입니다 sort(object).하지만 위의 질문에서 논의 된 문제로 인해 Hadley가 왜 그렇게했는지 이해합니다.


답변

더크의 대답은 훌륭합니다. 또한 data.frames 및 data.tables 를 인덱싱하는 데 사용되는 구문의 주요 차이점을 강조합니다 .

## The data.frame way
dd[with(dd, order(-z, b)), ]

## The data.table way: (7 fewer characters, but that's not the important bit)
dd[order(-z, b)]

두 통화의 차이는 작지만 중요한 결과를 초래할 수 있습니다. 특히 생산 코드를 작성하거나 연구의 정확성에 관심이있는 경우 변수 이름의 불필요한 반복을 피하는 것이 가장 좋습니다. data.table
이렇게 도와줍니다.

다음은 변수 이름의 반복으로 인해 문제가 발생할 수있는 예입니다.

Dirk의 답변에서 컨텍스트를 변경하고 이것이 많은 객체 이름이 있고 길고 의미있는 더 큰 프로젝트의 일부라고 가정 해 봅시다. 대신 dd이라고 quarterlyreport합니다. 그것은된다 :

quarterlyreport[with(quarterlyreport,order(-z,b)),]

알았어 괜찮아. 아무 문제가 없습니다. 다음으로 상사는 보고서에 지난 분기의 보고서를 포함하도록 요청합니다. 코드를 통해 lastquarterlyreport다양한 장소에 객체 를 추가 하고 어떻게 든 지구상에서 어떻게해야합니까?

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

그것은 당신이 의미하는 것이 아니지만 당신이 그것을 빨리하고 비슷한 코드의 페이지에 중첩되어 있기 때문에 그것을 발견하지 못했습니다. R은 그것이 당신이 의도 한 것이라고 생각하기 때문에 코드가 넘어지지 않습니다 (경고 및 오류 없음). 보고서를 읽는 사람이 그것을 발견하길 바라지 만 그렇지 않을 수도 있습니다. 프로그래밍 언어를 많이 사용한다면이 상황은 모두에게 익숙 할 것입니다. 당신이 말할 “오타”였습니다. 상사에게 말할 “오타”를 고칠 게요.

data.table우리는 이와 같은 작은 세부 사항에 대해 걱정하고 있습니다. 따라서 변수 이름을 두 번 입력하지 않도록 간단한 작업을 수행했습니다. 매우 간단한 것. 이미 자동으로 i프레임 내에서 평가 dd됩니다. 당신은 전혀 필요하지 않습니다 with().

대신에

dd[with(dd, order(-z, b)), ]

그냥

dd[order(-z, b)]

그리고 대신

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

그냥

quarterlyreport[order(-z,b)]

그것은 아주 작은 차이이지만, 하루 만에 목을 구할 수도 있습니다. 이 질문에 대한 다른 답변을 계량 할 때 결정의 기준 중 하나로 변수 이름의 반복을 계산하는 것을 고려하십시오. 일부 답변은 반복이 많고 다른 답변은 없습니다.


답변

여기에는 훌륭한 답변이 많이 있지만 dplyr 은 빠르고 쉽게 기억할 수있는 유일한 구문을 제공합니다 (따라서 매우 자주 사용).

library(dplyr)
# sort mtcars by mpg, ascending... use desc(mpg) for descending
arrange(mtcars, mpg)
# sort mtcars first by mpg, then by cyl, then by wt)
arrange(mtcars , mpg, cyl, wt)

OP의 문제 :

arrange(dd, desc(z),  b)

    b x y z
1 Low C 9 2
2 Med D 3 1
3  Hi A 8 1
4  Hi A 9 1


답변

R 패키지 data.table는 간단한 구문으로 Matt. 빠른 속도메모리 효율적인 data.tables 순서를 제공 합니다 (Mat가 그의 대답에서 아주 잘 강조 표시 했습니다 ). 그 이후로 많은 개선과 새로운 기능이 setorder()있었습니다. 에서 v1.9.5+, setorder()또한 작동 data.frames .

먼저 충분히 큰 데이터 세트를 만들고 다른 답변에서 언급 한 다양한 방법을 벤치마킹 한 다음 data.table 의 기능을 나열 합니다.

데이터:

require(plyr)
require(doBy)
require(data.table)
require(dplyr)
require(taRifx)

set.seed(45L)
dat = data.frame(b = as.factor(sample(c("Hi", "Med", "Low"), 1e8, TRUE)),
                 x = sample(c("A", "D", "C"), 1e8, TRUE),
                 y = sample(100, 1e8, TRUE),
                 z = sample(5, 1e8, TRUE),
                 stringsAsFactors = FALSE)

벤치 마크 :

보고 된 타이밍은 system.time(...)아래에 표시된 이러한 기능 에서 실행 된 것입니다. 타이밍은 가장 느리거나 빠른 순서로 아래에 표로 표시되어 있습니다.

orderBy( ~ -z + b, data = dat)     ## doBy
plyr::arrange(dat, desc(z), b)     ## plyr
arrange(dat, desc(z), b)           ## dplyr
sort(dat, f = ~ -z + b)            ## taRifx
dat[with(dat, order(-z, b)), ]     ## base R

# convert to data.table, by reference
setDT(dat)

dat[order(-z, b)]                  ## data.table, base R like syntax
setorder(dat, -z, b)               ## data.table, using setorder()
                                   ## setorder() now also works with data.frames 

# R-session memory usage (BEFORE) = ~2GB (size of 'dat')
# ------------------------------------------------------------
# Package      function    Time (s)  Peak memory   Memory used
# ------------------------------------------------------------
# doBy          orderBy      409.7        6.7 GB        4.7 GB
# taRifx           sort      400.8        6.7 GB        4.7 GB
# plyr          arrange      318.8        5.6 GB        3.6 GB 
# base R          order      299.0        5.6 GB        3.6 GB
# dplyr         arrange       62.7        4.2 GB        2.2 GB
# ------------------------------------------------------------
# data.table      order        6.2        4.2 GB        2.2 GB
# data.table   setorder        4.5        2.4 GB        0.4 GB
# ------------------------------------------------------------
  • data.tableDT[order(...)]구문은 다른 방법 중 가장 빠른 방법 ( ~) 보다 ~ 10 배 빠르지 만와dplyr 같은 양의 메모리를 소비합니다 dplyr.

  • data.table‘들 setorder()이었다 ~ 14 배 빠른 다른 방법 (가장 빠른보다는 dplyr하면서,) 단지 0.4GB 추가 메모리를 . dat이제 우리가 요구하는 순서대로되어 있습니다 (참조에 의해 업데이트 됨).

data.table 기능 :

속도:

  • data.table 의 순서는 기수 순서를 구현하기 때문에 매우 빠릅니다 .

  • 구문 DT[order(...)]data.table 의 빠른 순서도 사용하도록 내부적으로 최적화 됩니다. 친숙한 기본 R 구문을 계속 사용하면서 프로세스 속도를 높이고 메모리를 적게 사용할 수 있습니다.

기억:

  • 시간의 대부분은, 우리는 원래 필요하지 않습니다 data.frame 또는 data.table 재정렬 후를. 즉, 일반적으로 결과를 동일한 객체에 다시 할당합니다. 예를 들면 다음과 같습니다.

    DF <- DF[order(...)]

    문제는 원본 객체의 메모리에 최소 두 배 (2x)의 메모리가 필요하다는 것입니다. 로 메모리 효율 , data.table 때문에 또한 기능을 제공합니다 setorder().

    setorder()추가 사본을 만들지 않고 data.tables by reference ( in-place )를 재정렬 합니다. 하나의 열 크기와 동일한 추가 메모리 만 사용합니다.

다른 특징들:

  1. 그것은 지원 integer, logical, numeric, character심지어 bit64::integer64유형.

    하는 것으로 factor, Date, POSIXct등의 클래스는 모두 integer/ numeric유형 추가 속성을 아래에 따라서도 지원됩니다.

  2. 기수 R에서는 -문자형 벡터를 사용 하여 해당 열을 기준으로 내림차순으로 정렬 할 수 없습니다 . 대신 우리는를 사용해야 -xtfrm(.)합니다.

    그러나에 data.table , 우리는 단지 예를 들어, 수행 할 수 있습니다 dat[order(-x)]또는 setorder(dat, -x).


답변

R Wiki의 팁 섹션에 게시 된 Kevin Wright의이 (매우 유용한) 기능을 사용하면 쉽게 달성 할 수 있습니다.

sort(dd,by = ~ -z + b)
#     b x y z
# 4 Low C 9 2
# 2 Med D 3 1
# 1  Hi A 8 1
# 3  Hi A 9 1


답변

또는 doBy 패키지를 사용할 수 있습니다

library(doBy)
dd <- orderBy(~-z+b, data=dd)