[r] data.table에 키를 설정하는 목적은 무엇입니까?

data.table을 사용하고 있으며 키를 설정해야하는 많은 기능이 있습니다 (예 🙂 X[Y]. 따라서 데이터 테이블에서 키를 올바르게 설정하기 위해 키가 수행하는 작업을 이해하고 싶습니다.


내가 읽은 한 출처는 ?setkey.

setkey()a를 정렬하고 정렬 된 것으로 data.table표시합니다. 정렬 된 열이 핵심입니다. 키는 순서에 관계없이 모든 열이 될 수 있습니다. 열은 항상 오름차순으로 정렬됩니다. 표는 참조로 변경됩니다. 하나의 열만큼 큰 임시 작업 메모리 외에는 복사가 전혀 이루어지지 않습니다.

여기서 내 요점은 키가 data.table을 “정렬”하여 order(). 그러나 키를 갖는 목적을 설명하지 않습니다.


data.table FAQ 3.2 및 3.3은 다음을 설명합니다.

3.2 큰 테이블에는 키가 없지만 그룹화는 여전히 매우 빠릅니다. 왜 그런 겁니까?

data.table은 기수 정렬을 사용합니다. 이것은 다른 정렬 알고리즘보다 훨씬 빠릅니다. 기수는 특히 정수 전용 ?base::sort.list(x,method="radix")입니다.을 참조하십시오
. 이것은 또한 setkey()빠른 이유 중 하나
입니다. 키가 설정되지 않았거나 키의 순서와 다른 순서로 그룹화하는 경우이를 임시 기준이라고합니다.

3.3 키의 열을 기준으로 그룹화하는 것이 임시 기준보다 빠른 이유는 무엇입니까?

각 그룹은 RAM에서 연속적이므로 페이지 가져 오기를 최소화하고 메모리 memcpy를 C에서 반복하는 대신 대량으로 ( C에서) 복사 할 수 있기 때문입니다.

여기에서 키를 설정하면 R이 다른 알고리즘보다 “기수 정렬”을 사용할 수 있다고 생각하고 이것이 더 빠른 이유입니다.


10 분 빠른 시작 가이드에는 키에 대한 가이드도 있습니다.

data.frame, 특히 행 이름 (또는 영어로 된 행 이름)을 고려하여 시작하겠습니다. 즉, 단일 행에 속하는 여러 이름입니다. 단일 행에 속하는 여러 이름? 그것은 우리가 data.frame에서 익숙한 것이 아닙니다. 각 행에는 최대 하나의 이름이 있다는 것을 알고 있습니다. 사람은 적어도 두 개의 이름, 첫 번째 이름과 두 번째 이름을 가지고 있습니다. 예를 들어 성, 이름 순으로 정렬되는 전화 번호부를 구성하는 데 유용합니다. 그러나 data.frame의 각 행은 하나의 이름 만 가질 수 있습니다.

키는 단순히 문자가 아닌 정수, 인수, 문자 또는 기타 클래스 일 수있는 하나 이상의 행 이름 열로 구성됩니다. 또한 행은 키별로 정렬됩니다. 따라서 data.table은 여러 가지 방법으로 정렬 할 수 없기 때문에 최대 하나의 키를 가질 수 있습니다.

고유성이 적용되지 않습니다. 즉, 중복 키 값이 허용됩니다. 행이 키별로 정렬되어 있으므로 키의 중복 항목이 연속적으로 나타납니다.

전화 번호부는 키가 무엇인지 이해하는 데 도움이되었지만 요소 열이있는 것과 비교할 때 키가 다르지 않은 것 같습니다. 또한 키가 필요한 이유 (특히 특정 기능 사용)와 키로 설정할 열을 선택하는 방법에 대해서는 설명하지 않습니다. 또한 시간을 열로 설정하는 data.table에서 다른 열을 키로 설정하면 시간 열도 엉망이 될 것이므로 다른 열을 키. 누군가 제발 나를 깨달을 수 있습니까?



답변

마이너 업데이트 : 새 HTML 비 네트 도 참조하십시오 . 이 문제 는 우리가 계획하고있는 다른 비 네트를 강조합니다.


ad-hoc 조인도 on=허용 하는 새로운 기능을 고려 하여이 답변을 다시 업데이트했습니다 (2016 년 2 월) . 이전 (오래된) 답변에 대한 기록을 참조하십시오.

정확히 무엇을 setkey(DT, a, b)합니까?

두 가지 작업을 수행합니다.

  1. 재 배열의 행 data.table를 DT 제공하는 컬럼 (들) (기준 , B ) 를 참조하여 항상에서 증가하는 순서.
  2. 라는 속성을 설정하여 해당 열을 열로 표시 sorted합니다 DT.

재정렬은 빠르고 ( data.table 의 내부 기수 정렬 로 인해 ) 메모리 효율적입니다 ( double 유형의 추가 열 하나만 할당 됨).

언제 setkey()필요합니까?

그룹화 작업의 setkey()경우 절대 요구 사항이 아닙니다. 즉, 우리는 cold-by 또는 adhoc-by를 수행 할 수 있습니다 .

## "cold" by
require(data.table)
DT <- data.table(x=rep(1:5, each=2), y=1:10)
DT[, mean(y), by=x] # no key is set, order of groups preserved in result

그러나 이전에는 v1.9.6에서 설정 x[i]해야하는 양식의 조인이 필요 key했습니다 x. v1.9.6 + 의 새로운 on=인수를 사용하면 더 이상 사실이 아니며 키 설정도 여기서 절대적인 요구 사항이 아닙니다 .

## joins using < v1.9.6 
setkey(X, a) # absolutely required
setkey(Y, a) # not absolutely required as long as 'a' is the first column
X[Y]

## joins using v1.9.6+
X[Y, on="a"]
# or if the column names are x_a and y_a respectively
X[Y, on=c("x_a" = "y_a")]

참고 on=인수가 명시 적으로도 지정할 수 있습니다 keyed뿐만 아니라 조인.

key절대적으로 설정 해야하는 유일한 작업 은 foverlaps () 함수입니다. 그러나 우리는 완료되면이 요구 사항을 제거 할 더 많은 기능을 작업하고 있습니다.

  • 그렇다면 on=논쟁 을 구현하는 이유는 무엇 입니까?

    몇 가지 이유가 있습니다.

    1. 두 개의 data.tables를 포함하는 작업으로 작업을 명확하게 구분할 수 있습니다 . X[Y]변수의 이름을 적절하게 지정하면 명확해질 수 있지만 그렇게하는 것만으로는 이를 구분할 수 없습니다.

    2. 또한 해당 코드 줄을보고 해당 줄로 추적 할 필요없이 조인 / 하위 집합 이 즉시 수행 되는 열을 이해할 수 있습니다 setkey().

    3. 열이 참조의해 추가되거나 업데이트 되는 작업에서는 열 on=을 추가 / 업데이트하기 위해 전체 data.table을 재정렬 할 필요가 없기 때문에 작업이 훨씬 더 성능이 좋습니다. 예를 들면

      ## compare 
      setkey(X, a, b) # why physically reorder X to just add/update a column?
      X[Y, col := i.val]
      
      ## to
      X[Y, col := i.val, on=c("a", "b")]

      두 번째 경우에는 다시 주문할 필요가 없습니다. 시간이 많이 걸리는 순서를 계산하는 것이 아니라 물리적으로 RAM에서 data.table의 순서를 변경하고이를 피함으로써 원래 순서를 유지하며 성능도 뛰어납니다.

    4. 당신이있는 거 성능이 반복적으로 결합하지 않는 한해도 그렇지,하는 사이에 띄는 성능 차이가 없을 것 키 입력임시 합류했다.

이것은 data.table 을 키잉하면 더 이상 어떤 이점이 있습니까?

  • data.table 키를 지정하면 이점이 있습니까?

    data.table에 키를 지정하면 RAM의 해당 열을 기반으로 물리적으로 재정렬됩니다. 주문을 계산하는 것은 일반적으로 시간이 많이 걸리는 부분이 아니라 재정렬 자체입니다. 그러나 데이터를 RAM에서 정렬하면 동일한 그룹에 속하는 행이 모두 RAM에서 연속적이기 때문에 캐시 효율성이 매우 높습니다. 키가 지정된 데이터에 대한 작업 속도를 높이는 것은 정렬입니다.

    따라서 전체 data.table을 재정렬하는 데 소요 된 시간이 캐시 효율적인 조인 / 집계를 수행하는 데 소요되는 시간이 가치가 있는지 파악하는 것이 중요합니다. 일반적으로 동일한 키가 지정된 data.table 에 대해 반복적 인 그룹화 / 조인 작업이 수행 되지 않는 한 눈에 띄는 차이가 없어야합니다.

따라서 대부분의 경우 더 이상 키를 설정할 필요가 없습니다. on=키 설정으로 인해 악용하려는 성능이 크게 향상되지 않는 한 가능하면 사용하는 것이 좋습니다 .

질문 : 당신이에 비해 같은 성능 것 어떻게 생각하십니까 키가 당신이 사용하는 경우, 가입 setorder()(가) 순서를 data.table 사용 on=? 지금까지 따랐다면 알아낼 수있을 것입니다 :-).


답변

키는 기본적으로 데이터 세트에 대한 색인으로, 매우 빠르고 효율적인 정렬, 필터링 및 결합 작업이 가능합니다. 이것이 아마도 데이터 프레임 대신 데이터 테이블을 사용하는 가장 좋은 이유 일 것입니다 (데이터 테이블을 사용하는 구문도 훨씬 더 사용자 친화적이지만 키와는 관련이 없습니다).

색인을 이해하지 못하는 경우 다음을 고려하십시오. 전화 번호부는 이름으로 “색인화”됩니다. 그래서 누군가의 전화 번호를 찾고 싶다면 꽤 간단합니다. 하지만 전화 번호로 검색하고 싶다고 가정 해 보겠습니다 (예 : 특정 전화 번호를 가진 사람 조회)? 전화 번호로 전화 번호부를 “재색 인”할 수 없다면 시간이 오래 걸립니다.

다음 예를 고려하십시오. 미국의 모든 우편 번호 (> 33,000)와 관련 정보 (도시, 주, 인구, 중간 소득 등)의 ZIP 테이블이 있다고 가정합니다. 특정 우편 번호에 대한 정보를 조회하려면 먼저 검색 (필터)이 약 1000 배 더 빠릅니다 setkey(ZIP,zipcode).

또 다른 이점은 조인과 관련이 있습니다. 데이터 테이블 ( “PPL”이라고 부름)에 사람 목록과 우편 번호가 있고 ZIP 테이블의 정보 (예 : 도시, 주 등)를 추가하려고한다고 가정합니다. 다음 코드가이를 수행합니다.

setkey(ZIP,zipcode)
setkey(PPL,zipcode)
full.info <- PPL[ZIP, nomatch=F]

이것은 공통 필드 (우편 번호)를 기반으로 두 테이블의 정보를 결합한다는 의미에서 “결합”입니다. 매우 큰 테이블에서 이와 같은 조인은 데이터 프레임에서는 매우 느리고 데이터 테이블에서는 매우 빠릅니다. 실제 예에서는 전체 우편 번호 테이블에 대해 이와 같이 20,000 개 이상의 조인을 수행해야했습니다. 데이터 테이블을 사용하면 스크립트에 약 20 분이 걸렸습니다. 실행합니다. 데이터 프레임으로 시도조차하지 않았습니다. 2 주 이상 걸렸을 것입니다.

IMHO 당신은 단순히 읽는 것이 아니라 FAQ와 소개 자료를 공부 해야합니다 . 이것을 적용 할 실제 문제가 있는지 파악하는 것이 더 쉽습니다.

[@Frank의 의견에 대한 답변]

Re : 정렬 vs. 인덱싱이 질문 에 대한 답에 따르면setkey(...) 실제로 테이블의 열 (예 : 물리적 정렬)을 재 배열하고 데이터베이스 의미에서 인덱스를 생성하지 않는 것으로 보입니다 . 이것은 몇 가지 실질적인 의미를 가지고 있습니다. 한 가지는 테이블에 키를 설정 setkey(...)한 다음 키 열의 값을 변경하면 data.table은 sorted속성을 해제하여 테이블이 더 이상 정렬되지 않도록 선언합니다 . 적절한 정렬 순서를 유지하기 위해 동적으로 색인을 다시 생성 하지 않습니다 (데이터베이스에서 발생하는 것처럼). 또한를 사용하여 “키 제거” 는 테이블을 원래의 정렬되지 않은 순서로 복원 setky(DT,NULL)하지 않습니다 .

Re : 필터 대 조인 -실질적인 차이점은 필터링은 단일 데이터 집합에서 하위 집합을 추출하는 반면 조인은 공통 필드를 기반으로 두 데이터 집합의 데이터를 결합한다는 것입니다. 여러 종류의 조인 (내부, 외부, 왼쪽)이 있습니다. 위의 예는 내부 조인 (두 테이블에 공통된 키가있는 레코드 만 반환 됨)이며 필터링과 많은 유사점이 있습니다.


답변