Re: [問題] 依特定欄位的內容對data.table資料作分群

看板R_Language作者 (天)時間8年前 (2017/03/05 23:28), 8年前編輯推噓4(4011)
留言15則, 1人參與, 最新討論串2/2 (看更多)
※ 引述《joson4921 (特務)》之銘言: : : - 問題: : : [問題類型]: : : 程式諮詢(我想用R 做某件事情,但是我不知道要怎麼用R 寫出來) : : [軟體熟悉度]: : : 入門(寫過其他程式,只是對語法不熟悉) : : [問題敘述]: : : 手邊有一份資料 dt1 ,裡面有幾個欄位,以下節錄部分內容: : Stop.No TravelTime Weekend : 1 1   ↘ : 2 1   → 當Stop.No==1, TravelTime介於[0,120],Weekend可能為True/False : 3 1   ↗ : ... : 666 2   ↘ : 667 2   → 當Stop.No==2, TravelTime介於[60,180],Weekend可能為True/False : 668 2 ↗ : ... : 1315 3   ↘ : 1316 3  → 當Stop.No==3,TravelTime介於[120,240],Weekend可能為True/False : 1317 3  ↗ : ... : 2017 : ========================================================================= : ※1 共2017筆資料 : ※2 Stop.No的資料為隨機1~3, 上面表列僅為示意用 : 並非正好[ 第1~665筆的Stop.No==1, 第666~1314筆==2, 第1315~2017筆==3] : ========================================================================= : 我想做的事情是依照 Stop.No/Weekend 欄位進行分群, : 例: : 這2017筆資料中有322筆資料 Stop.No==1 且 Weekend=T , : 則將這322筆視為同一群,其餘依此類推,故應可得到6群 : 這邊參照之前板上前輩教導的方法將相同的 Stop.No 和 Weekend 取出作 group_by : 程式碼架構如下(by dplyr): : dt1 %>% : group_by( Stop.No, Weekend ) %>% : summarise( 對 group_by 出來的6群,作集群分析,詳如下述 ) : ========================================================================= : summarise() 的內容: : 想把「上面 group_by 出來的結果(6群),每一群都再分成兩群(gr1/gr2)並找出中心」, : 因此上網查了分群的方法,根據google,假設上述六群中的某群叫作 gr8, : 則可透過以下程式碼將 gr8 分成兩群,並求出 gr8 的群集中心 : kmeans.result = kmeans( gr8, 2) : gr8_result <- gr8[, centers := kmeans.result$centers[kmeans.result$cluster] ] : ========================================================================= : 我想做的事情: : 以本資料 dt1 為例, group_by 出來的 6群 應可透過kmeans求出 12個集群中心 : ( gr8 裡面的 centers欄位 ) : 最後將 gr8 的 centers 欄位內容加回 dt1 對應的2017筆資料列後面 : 以上敘述滿複雜的,若有敘述不清的地方歡迎提問,小弟將盡速補充 : 請求各位前輩們協助,先謝謝各位大大了 : : [程式範例]: : : [環境敘述]: : : : [關鍵字]: : : 上一篇發現做錯,先刪掉了 好讀版:http://pastebin.com/MguuwYAi 建議直接看好讀版 簡單simulation一組資料,有五個變數,兩個變數是group by的變數 另外三個是拿來分群的變數 最終的結果是把原始資料併上每個資料的cluster以及對應的center 計算時間的話,dplyr + tidyr 或是 data.table 都可以在0.1秒以內可以算完 20000個觀察值 library(dplyr) library(tidyr) library(pipeR) # dplyr + tidyr ngrp <- 2L numSamples <- 200L DF <- data.frame(V1 = sample(1L:3L, numSamples, TRUE), V2 = sample(1L:2L, numSamples, TRUE), V3 = rnorm(numSamples), V4 = rnorm(numSamples), V5 = rnorm(numSamples)) DF %>>% group_by(V1, V2) %>>% summarise( oriData = list(data.frame(V3 = V3, V4 = V4, V5 = V5)), kmeansRes = list(kmeans(data.frame(V3_center = V3, V4_center = V4, V5_center = V5), ngrp))) %>% rowwise %>>% do(V1 = .$V1, V2 = .$V2, oriData = .$oriData, grps = .$kmeansRes$cluster, centers = as.data.frame(.$kmeansRes$centers[.$kmeansRes$cluster, ])) %>>% mutate(V1 = unlist(V1), V2 = unlist(V2)) %>>% unnest(grps, oriData, centers) # # A tibble: 200 × 9 # V1 V2 grps V3 V4 V5 V3_center V4_center V5_center # <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> # 1 1 1 1 1.11016253 0.02722608 0.3027893 -0.35019463 0.1903187 0.9848824 # 2 1 1 1 0.70820726 -2.19334026 0.3242983 -0.35019463 0.1903187 0.9848824 # 3 1 1 2 -0.33493165 -0.07425543 -0.6383053 -0.02633612 0.3241840 -1.0112687 # 4 1 1 2 -2.03926090 0.24728959 0.1302806 -0.02633612 0.3241840 -1.0112687 # 5 1 1 2 -0.04744358 0.20338375 -1.3591982 -0.02633612 0.3241840 -1.0112687 # 6 1 1 2 0.12768265 1.34077790 -1.4590170 -0.02633612 0.3241840 -1.0112687 # 7 1 1 2 1.08012650 1.95067610 1.3336783 -0.02633612 0.3241840 -1.0112687 # 8 1 1 2 0.85677265 0.46973309 -0.6577587 -0.02633612 0.3241840 -1.0112687 # 9 1 1 1 0.90455630 2.44301533 -1.0511750 -0.35019463 0.1903187 0.9848824 # 10 1 1 2 1.07202152 -0.05310248 -1.4048170 -0.02633612 0.3241840 -1.0112687 # # ... with 190 more rows # data.table library(data.table) ngrp <- 2L numSamples <- 200L DT <- data.table(V1 = sample(1L:3L, numSamples, TRUE), V2 = sample(1L:2L, numSamples, TRUE), V3 = rnorm(numSamples), V4 = rnorm(numSamples), V5 = rnorm(numSamples)) DT_kmeans <- DT[ , .( oriData = list(data.table(V3, V4, V5)), kmeansRes = list(kmeans(data.table(V3, V4, V5) %>>% setnames(paste0(names(.), "_center")), ngrp) %>>% {data.table(grp = .$cluster, .$centers[.$cluster, ])})), by = .(V1, V2)] cbind(DT_kmeans[ , .(V1, V2)][rep(1L:nrow(DT_kmeans), sapply(DT_kmeans$oriData, nrow)), ], rbindlist(DT_kmeans$oriData), rbindlist(DT_kmeans$kmeansRes)) # V1 V2 V3 V4 V5 grp V3_center V4_center V5_center # 1: 1 1 -1.5910524 2.1274208 -1.3464532 1 0.1155321 0.9482175 0.290026606 # 2: 1 1 0.3280774 -0.1150860 -1.2363502 2 -0.1138527 -1.1174780 -0.603929772 # 3: 1 1 -0.2026653 -0.9188654 0.4275579 2 -0.1138527 -1.1174780 -0.603929772 # 4: 1 1 -0.3258952 0.3159080 1.3667256 1 0.1155321 0.9482175 0.290026606 # 5: 1 1 -0.2819986 -1.2371227 0.6597289 2 -0.1138527 -1.1174780 -0.603929772 # --- # 196: 2 2 0.6875229 -1.4182973 -0.1774791 1 0.5244143 -0.4805122 0.084895890 # 197: 2 2 1.1732799 -0.7428654 -0.1777401 1 0.5244143 -0.4805122 0.084895890 # 198: 2 2 -1.1992074 -0.6165810 0.0421549 2 -0.8536335 0.6373769 -0.007181771 # 199: 2 2 -0.4136762 1.1002398 0.4306150 2 -0.8536335 0.6373769 -0.007181771 # 200: 2 2 -0.1929361 -0.7346460 -0.4713694 1 0.5244143 -0.4805122 0.084895890 -- R資料整理套件系列文: magrittr #1LhSWhpH (R_Language) https://goo.gl/72l1m9 data.table #1LhW7Tvj (R_Language) https://goo.gl/PZa6Ue dplyr(上.下) #1LhpJCfB,#1Lhw8b-s (R_Language) https://goo.gl/I5xX9b tidyr #1Liqls1R (R_Language) https://goo.gl/i7yzAz pipeR #1NXESRm5 (R_Language) https://goo.gl/zRUISx -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.232.184.141 ※ 文章網址: https://www.ptt.cc/bbs/R_Language/M.1488727704.A.05E.html

03/05 23:44, , 1F
額..我一直以為有用到 %>% 就是dplyr,不過似乎不是..
03/05 23:44, 1F
%>%是 magrittr提供的,非本來dplyr就有,原本dplyr要推%.%這個operator 結果magrittr作者做出來之後,Hadley就直接import magrittr的%>%了 只是%>%,我自己在實務上有遇到一些奇怪的問題,後來就都改用%>>%了 其實dplyr是包含了group_by, summarise, mutate, rowwise, do那些函數~~ unnest是在tidyr裡面

03/05 23:45, , 2F
正在跑c大大的程式碼,先謝謝c大,若有後續再上來留言><
03/05 23:45, 2F
先測試看看吧,因為你沒直接給我資料全貌,我就只好自己生了~~ 就以簡潔度來說,我覺得data.table寫起來應該是最輕鬆的 只是不容易去想怎麼做,要想比較久Orz,而且後面那段copy V1,V2是參考unnest的做法 data.table做起來會需要比較多的技巧Orz,包含把kmeans結果整理成data.table... dplyr可能做法比較直觀,但是需要掌握rowwise, do的用法才有辦法 比較起來,兩者都有利有弊,端看使用者喜歡哪一種

03/05 23:50, , 3F
不好意思>"<,想說節錄部份就好,怕原檔上來太亂哈哈
03/05 23:50, 3F
可以把原檔放在某個免空,用csv提供,說明用節錄即可

03/06 00:10, , 4F
https://goo.gl/3IkkqK 不知道怎麼搞得Weekend那一欄有
03/06 00:10, 4F

03/06 00:11, , 5F
點問題沒辦法輸出,所以這個檔案裡面只有Stop.No 欄位和
03/06 00:11, 5F

03/06 00:13, , 6F
TravelTime欄位,以這個資料來說若用 Stop.No來group_by
03/06 00:13, 6F

03/06 00:14, , 7F
應該會分成Stop_No.=1~45共45組,每組都用kmeans()下去
03/06 00:14, 7F

03/06 00:15, , 8F
切成兩組的話應該會產生90個群集中心,先來研究完c大的
03/06 00:15, 8F

03/06 00:16, , 9F
程式碼再來看要怎麼改0.0
03/06 00:16, 9F
是九十組沒錯,所以我的example code是產生12組center 不過你說要併回原本的資料,我就一次做完了... 要求那12組centers就只能取distinct or unique的 V1, V2, grp, V3_center, V4_center, V5_center了

03/06 00:49, , 10F
沒關係c大一次做完也好,我就看看要怎麼改才能符合自己
03/06 00:49, 10F

03/06 00:49, , 11F
的需求,真的還是卡住再上來求教,再次謝謝c大!
03/06 00:49, 11F

03/08 03:27, , 12F
鑽研了兩天..還是得回來請問c大,data.table那個做法裡
03/08 03:27, 12F

03/08 03:28, , 13F
面cbind的部份看了兩天還是有看沒懂,望c大指點迷津@@"
03/08 03:28, 13F
DT_kmeans 看懂就可以看懂cbind了 DT_kmeans 裡面第一個部分: oriData = list(data.table(V3, V4, V5)) 其實是創一個欄位儲存list of data.table => 用來存原始資料 因為有group by所以可以想像裡面每一個data.table都會超過一個列 第二個部分: kmeansRes = list(kmeans(data.table(V3, V4, V5) %>>% setnames(paste0(names(.), "_center")), ngrp) %>>% {data.table(grp = .$cluster, .$centers[.$cluster, ])})) 逐步拆解pipe來看 kmeans(data.table(V3, V4, V5) %>>% setnames(paste0(names(.), "_center")), ngrp) 這部分就是放入一個data.table,並設定名字,去train kmeans,#cluster = ngrp 第二個pipe後面: {data.table(grp = .$cluster, .$centers[.$cluster, ])} pipe進來的東西是kmeans的結果,所以 grp是kmeans結果中的所在cluster,那後面.$centers[.$cluster, ]就是各群的center了 解析完DT_kmeans,接下來看回cbind,第一塊: DT_kmeans[ , .(V1, V2)][rep(1L:nrow(DT_kmeans), sapply(DT_kmeans$oriData, nrow)), ] 第一個[]是取V1, V2欄,後面就是根據group by後各組資料的長度將V1,V2列做複製 所以最後第一塊的長度會是原始資料的長度 第二三塊就是把各組的原始資料、kmeans結果展開合併成一個data.table 最後再全部cbind起來就可以得到結果了 ※ 編輯: celestialgod (36.232.184.141), 03/08/2017 20:35:22

03/08 22:18, , 14F
啊啊..那個果然是根據長度,看來還是得用之前那個破方法
03/08 22:18, 14F

03/08 22:19, , 15F
來搞了,因為是要依對應欄位回填對應值...QQ
03/08 22:19, 15F
文章代碼(AID): #1Ol2wO1U (R_Language)
文章代碼(AID): #1Ol2wO1U (R_Language)