作者celestialgod (天)
看板R_Language
标题Re: [问题] 依特定栏位的内容对data.table资料作分群
时间Sun Mar 5 23:28:20 2017
※ 引述《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://webptt.com/cn.aspx?n=bbs/R_Language/M.1488727704.A.05E.html
1F:推 joson4921: 额..我一直以为有用到 %>% 就是dplyr,不过似乎不是.. 03/05 23:44
%>%是 magrittr提供的,非本来dplyr就有,原本dplyr要推%.%这个operator
结果magrittr作者做出来之後,Hadley就直接import magrittr的%>%了
只是%>%,我自己在实务上有遇到一些奇怪的问题,後来就都改用%>>%了
其实dplyr是包含了group_by, summarise, mutate, rowwise, do那些函数~~
unnest是在tidyr里面
2F:→ joson4921: 正在跑c大大的程式码,先谢谢c大,若有後续再上来留言>< 03/05 23:45
先测试看看吧,因为你没直接给我资料全貌,我就只好自己生了~~
就以简洁度来说,我觉得data.table写起来应该是最轻松的
只是不容易去想怎麽做,要想比较久Orz,而且後面那段copy V1,V2是参考unnest的做法
data.table做起来会需要比较多的技巧Orz,包含把kmeans结果整理成data.table...
dplyr可能做法比较直观,但是需要掌握rowwise, do的用法才有办法
比较起来,两者都有利有弊,端看使用者喜欢哪一种
3F:推 joson4921: 不好意思>"<,想说节录部份就好,怕原档上来太乱哈哈 03/05 23:50
可以把原档放在某个免空,用csv提供,说明用节录即可
5F:→ joson4921: 点问题没办法输出,所以这个档案里面只有Stop.No 栏位和 03/06 00:11
6F:→ joson4921: TravelTime栏位,以这个资料来说若用 Stop.No来group_by 03/06 00:13
7F:→ joson4921: 应该会分成Stop_No.=1~45共45组,每组都用kmeans()下去 03/06 00:14
8F:→ joson4921: 切成两组的话应该会产生90个群集中心,先来研究完c大的 03/06 00:15
9F:→ joson4921: 程式码再来看要怎麽改0.0 03/06 00:16
是九十组没错,所以我的example code是产生12组center
不过你说要并回原本的资料,我就一次做完了...
要求那12组centers就只能取distinct or unique的
V1, V2, grp, V3_center, V4_center, V5_center了
10F:推 joson4921: 没关系c大一次做完也好,我就看看要怎麽改才能符合自己 03/06 00:49
11F:→ joson4921: 的需求,真的还是卡住再上来求教,再次谢谢c大! 03/06 00:49
12F:推 joson4921: 钻研了两天..还是得回来请问c大,data.table那个做法里 03/08 03:27
13F:→ joson4921: 面cbind的部份看了两天还是有看没懂,望c大指点迷津@@" 03/08 03:28
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
14F:→ joson4921: 啊啊..那个果然是根据长度,看来还是得用之前那个破方法 03/08 22:18
15F:→ joson4921: 来搞了,因为是要依对应栏位回填对应值...QQ 03/08 22:19