Key(主鍵)和基於子集的快速檢索

主鍵

什麼是主鍵

 

在前面我們討論瞭如何用“i”進行提取子集的方法,這節我們採用另一種方法,用主鍵(key)來提取子集。

開始,我們先看一個data.frame,每個data.frame都有一個行名稱,先看下面一個data.frame DF :

> set.seed(1L)
> DF = data.frame(ID1 = sample(letters[1:2], 10, TRUE),
+                 ID2 = sample(1:3, 10, TRUE),
+                 val = sample(10),
+                 stringsAsFactors = FALSE,
+                 row.names = sample(LETTERS[1:10]))
> DF

  ID1 ID2 val
C   a   3   5
D   a   1   6
E   b   2   4
G   a   1   2
B   b   1  10
H   a   2   8
I   b   1   9
F   b   2   1
J   a   3   7
A   b   2   3

> rownames(DF)
 [1] "C" "D" "E" "G" "B" "H" "I" "F" "J" "A"

 

我們可以通過它的行名提取這一行,如下:

> DF["C", ]
  ID1 ID2 val
C   a   3   5

 

行名或多或少就像數據框中行的一個變量,但是:

1 每行只能有一個行名,但是,打個比方,每個人至少有兩個名字,first name 和second name,就如查找一個電話簿,先查second name 再查first name ,這是非常有用的。用數據框就做不到這個,因爲他只能支持一個行名稱。

2  行名還是唯一的。不能出現重複的行名。

> rownames(DF) = sample(LETTERS[1:5], 10, TRUE)  #行的名稱不能重複
Error in `row.names<-.data.frame`(`*tmp*`, value = value) :
  不允許有重複的'row.names'
In addition: Warning message:
non-unique values when setting 'row.names': ‘C’, ‘D’

現在我們將DT轉化爲data.table:

> DT = as.data.table(DF)
> DT

    ID1 ID2 val
 1:   a   3   5
 2:   a   1   6
 3:   b   2   4
 4:   a   1   2
 5:   b   1  10
 6:   a   2   8
 7:   b   1   9
 8:   b   2   1
 9:   a   3   7
10:   b   2   3
> rownames(DT)
 [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"

 

1  現在行名已經被重置

2   Data.table不使用行名。因爲data.table是衍化自data.frame,它仍舊保留着行名的特性,但是從來不使用行名,一會兒再探討其中的原因。如果你想保留行名,可以使用keep.rownames = TRUE 在轉化過程中,as.data.table()。它會產生一個新的列rn,內容是列名。

 

主鍵和它的特性

 

1.我們可以設置多個主鍵,在多維數據中,而且數據類型可以是字符型、整型、數字型、因子型,但不支持列表和混合類型

2.不強制主鍵的唯一性。複製主鍵是被允許的。

3.設置主鍵,可以做兩件事:

   a.對列進行升序排列

   b.將這些列設置爲主鍵列,data.table根據主鍵列進行排序,sorted()

 

因爲行是重新排序的,所以至多隻有一個主鍵,因爲不能進行排序。

 

 

設置、取得和使用主鍵

 

1.如何設置data.table flights中的列origin爲主鍵

> setkey(flights, origin)   #這在交互界面下使用,飛鏟更方便
> head(flights[,-(1:6)])

   arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
1:         0         0      AA  N3DEAA    119    EWR  LAX      339     2454   18  24
2:       -17         0      AA  N5CFAA    172    EWR  MIA      161     1085   16  55
3:       185         0      AA  N471AA    300    EWR  DFW      214     1372   16  11
4:        -2         0      AA  N4WNAA    320    EWR  DFW      214     1372   14  49
5:       -10         0      AA  N5DMAA   1205    EWR  MIA      154     1085    6   7
6:       -17         0      AA  N491AA   1223    EWR  DFW      215     1372    9  49

另一種方法:

setkeyv(flights, "origin")  #在函數中使用比較方便

設置完主鍵後,flights被自動修改,並不會產生中間data.table,因此,非常的有效率,且節省空間。

在建立data.table時也可以直接設置主鍵,採用參數key,內容是列名字,字符型向量。

 

設置“*”和“:=”

設置origin爲主鍵以後,可以按照行提取子集:

 

> flights[.("JFK"),-(1:6)]
       arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
    1:        13         0      AA  N338AA      1    JFK  LAX      359     2475    9  14
    2:        13         0      AA  N335AA      3    JFK  LAX      363     2475   11  57
    3:         9         0      AA  N327AA     21    JFK  LAX      351     2475   19   2
    4:         1         0      AA  N319AA    117    JFK  LAX      350     2475   13  47
    5:       -18         0      AA  N323AA    185    JFK  LAX      338     2475   21  33
   ---                                                                                 
81479:       -21         0      UA  N596UA    512    JFK  SFO      337     2586   17   5
81480:       -37         0      UA  N568UA    514    JFK  SFO      344     2586   18  27
81481:       -33         0      UA  N518UA    535    JFK  LAX      320     2475   17  53
81482:       -38         0      UA  N512UA    541    JFK  SFO      343     2586    9  24
81483:       -38         0      UA  N590UA    703    JFK  LAX      323     2475   11  24

flights[J("JFK")]

flights[list("JFK")]

flights["JFK"]

flights[c("JFK", "LGA")]

 

如何返回data.table中被設置爲主鍵的列?

> key(flights)
[1] "origin"

 

如何同時設置兩列爲主鍵?

> setkey(flights, origin, dest)
> head(flights[,-(1:6)])
   
   arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
1:       -25         0      EV  N11547   4373    EWR  ALB       30      143    7  24
2:        79         0      EV  N18120   4470    EWR  ALB       29      143   23  13
3:       211         0      EV  N11184   4373    EWR  ALB       32      143   15  26
4:        19         0      EV  N14905   4551    EWR  ALB       32      143    7  55
5:        42         0      EV  N19966   4470    EWR  ALB       26      143    8  17
6:        62         0      EV  N19966   4682    EWR  ALB       31      143   23   1
> key(flights)
[1] "origin" "dest"   

它首先根據列"origin"進行排序,然後再根據列"dest"進行排序。比如origin第一個值是EWR,則對應的dest下面的值依次是ALB,B…,C…,一直到z,然後origin取值B,dest取值ewr,b,c等等。

 

根據主鍵列origin=JFK且dest=MIA篩選所有的行

> flights[.("JFK", "MIA"),-(1:6)]
      arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
   1:       -17         0      AA  N5FJAA    145    JFK  MIA      161     1089   15   9
   2:        -8         0      AA  N5DWAA   1085    JFK  MIA      166     1089    9  17
   3:        -1         0      AA  N635AA   1697    JFK  MIA      164     1089   12  27
   4:         3         0      AA  N5CGAA   2243    JFK  MIA      157     1089    5  46
   5:       -12         0      AA  N397AA   2351    JFK  MIA      154     1089   17  36
  ---                                                                                 
2746:       -22         0      AA  N5FNAA   2351    JFK  MIA      148     1089   16  59
2747:       -20         0      AA  N5EYAA   1085    JFK  MIA      146     1089    8  26
2748:       -17         0      AA  N5BTAA   1101    JFK  MIA      150     1089    6  47
2749:       -12         0      AA  N3ETAA   2299    JFK  MIA      150     1089    5  42
2750:         4         0      AA  N5FSAA   2387    JFK  MIA      146     1089   19  44

 

這個語句內部是如何工作的呢?

首先匹配JFK,篩選出子集,然後再匹配MIA,得出兩者都匹配的數據集。

 

選擇滿足第一個主鍵origin="JFK"的所有行

> key(flights)
[1] "origin" "dest" 
> flights[.("JFK")] ## or in this case simply flights["JFK"], for convenience
       arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
    1:         4         0      B6  N766JB     65    JFK  ABQ      280     1826   20  11
    2:       161         0      B6  N507JB     65    JFK  ABQ      252     1826   22  15
    3:         6         0      B6  N652JB     65    JFK  ABQ      269     1826   20   6
    4:       -15         0      B6  N613JB     65    JFK  ABQ      259     1826   20   9
    5:        32         0      B6  N598JB     65    JFK  ABQ      267     1826   20  39
   ---                                                                                 
81479:       -18         0      DL  N915AT   2165    JFK  TPA      142     1005    8   0
81480:        -8         0      B6  N516JB    225    JFK  TPA      149     1005   19  32
81481:       -22         0      B6  N334JB    325    JFK  TPA      145     1005   14  43
81482:        -5         0      B6  N637JB    925    JFK  TPA      149     1005    9  57
81483:       -18         0      B6  N595JB   1025    JFK  TPA      145     1005    8  31

 

選擇滿足第二個主鍵dest="MIA"的所有行

> flights[.(unique(origin), "MIA")]

因爲不能跳過第一個主鍵,所以,需要把第一個主鍵的值設置爲唯一。----目前,還沒有想的通其中過的道理。

 

 

結合主鍵,j\by

 

a.選擇j

 

選擇滿足主鍵分別等於“LGA”,“TPA”,列名爲“arr_delay”的所有行

 

> key(flights)
[1] "origin" "dest" 
> flights[.("LGA", "TPA"), .(arr_delay)]
      arr_delay
   1:         1
   2:        14
   3:       -17
   4:        -4
   5:       -12
  ---         
1848:        39
1849:       -24
1850:       -12
1851:        21
1852:       -11

 

也可以使用以下代碼,效果與上面一樣:

> flights[.("LGA","TPA"),"arr_delay",with=FALSE]

b.鏈接

在a結果的基礎上,對列進行排序結果如下:

> flights[.("LGA","TPA"),.(arr_delay)][order(-arr_delay)]
      arr_delay
   1:       486
   2:       380
   3:       351
   4:       318
   5:       300
  ---         
1848:       -40
1849:       -43
1850:       -46
1851:       -48
1852:       -49

 

c.對j進行計算

在滿足origin = "LGA" and dest = "TP"的子集上,計算arr_delay的最大值

> flights[.("LGA","TPA"),max(arr_delay)]
[1] 486

 

d.通過引用進行子集替代,在 j 上使用:=

 

查找所有不同的時間(hour

> flights[,sort(unique(hour))]
 [1]  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

 

結果,共有24個不同時間,023都在,現在用0代替23。這次的方法是,用主鍵實現:

> setkey(flights, hour)
> key(flights)

[1] "hour"

> flights[.(23), hour := 0L]
> key(flights)

NULL
> flights[, sort(unique(hour))]
 [1]  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22

 

當用0代替23後,hour列已經不再按照順序排列,所以,key(flights),返回值爲NULL

 

e.使用“by”分組

先設置主鍵:

> setkey(flights,origin,dest)
> key(flights)

[1] "origin" "dest"

 

計算origin=“JFK”,每月dep_delay的最大值,並按月排序

> ans <- flights["JFK", max(dep_delay), keyby = month]
> head(ans)

   month   V1
1:     1  881
2:     2 1014
3:     3  920
4:     4 1241
5:     5  853
6:     6  798
> key(ans)
[1] "month"

結果顯示,主鍵變爲month.

 

3)附加的參數“mult”、“nomatch”

 

a.mult參數

返回滿足條件的第一行

> flights[.("JFK", "MIA"), mult = "first"]
   year month day dep_time dep_delay arr_time arr_delay cancelled carrier tailnum flight origin dest air_time

1: 2014     1   1      546         6      853         3         0      AA  N5CGAA   2243    JFK  MIA      157    

 

返回滿足條件的最後一行

> flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last"]
   year month day dep_time dep_delay arr_time arr_delay cancelled carrier tailnum flight origin dest air_time distance hour min
1: 2014     5  23     1803       163     2003       148         0      MQ  N515MQ   3553    LGA  XNA      158     2:   NA    NA  NA       NA        NA       NA        NA        NA      NA      NA     NA    JFK  XNA       NA      
3: 2014     2   3     1208       231     1516       268         0      EV  N14148   4419    EWR  XNA      184    

 

沒有滿足JFK 和 XNA 的行,故返回值都是NA

 

b.nomatch參數

> flights[.(c("LGA", "JFK", "EWR"), "XNA"), mult = "last", nomatch = 0L]
   year month day dep_time dep_delay arr_time arr_delay cancelled carrier tailnum flight origin dest air_time 1: 2014     5  23     1803       163     2003       148         0      MQ  N515MQ   3553    LGA  XNA      158     2: 2014     2   3     1208       231     1516       268         0      EV  N14148   4419    EWR  XNA      184    

 

nomatch 默認值是NA,當設置nomatch=0L後,不匹配的列都被跳過,不予顯示。因此,只返回了兩行結果。

 

4)二進制檢索與矢量掃描

 

> flights[.("JFK", "MIA")]

> flights[origin == "JFK" & dest == "MIA"]

 

比較兩種檢索方法,

> t1 <- system.time(ans1 <- DT[x == "g" & y == 877L])
> t1

 用戶  系統  流逝
 0.33  0.28 10.16
> dim(t1)
NULL
> dim(ans1)
[1] 761   3
> t2 <- system.time(ans2 <- DT[.("g", 877L)])
> t2

用戶 系統 流逝
0.01 0.00 0.13
> dim(ans2)
[1] 761   3

 

第二種比第一種速度提高了78倍。很明顯了!

原因:

第一種即矢量掃描,所有的數據都要遍歷一遍,且返回邏輯值,TRUE or FALSE ,兩列,然後再比較兩列是否相等。速度很長的慢。

第二種,即二進制檢索,以以下的例子爲證:

1, 5, 10, 19, 22, 23, 30

數據已經被排序了,同主鍵的特徵,如果要尋找值等於1 的行,

  (1)從中位數19開始,19==1?否,19>1

 (2)所以大於19的值都不再進行檢索

   (3)選取1到19的中位數5,5==1?否,5>1

 (4)選取1-5的數,1==1?是,完成。

遍歷次數比第一種方法少很多,因此速度提升很快。

 

所以,通過使用關鍵詞進行檢索,是非常高效的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章