作者daze (一期一会)
看板R_Language
标题[问题] 如何一次检查多个column的字串
时间Mon Sep 7 12:44:18 2020
[问题类型]:
程式谘询(我想用R 做某件事情,但是我不知道要怎麽用R 写出来)
[软体熟悉度]:
入门(写过其他程式,只是对语法不熟悉)
[问题叙述]:
代码储存在复数栏位中,希望检查是否有某些栏位符合特定字串规则,输出True or False
目前用 dplyr::filter_at 是可以得到需要的结果
但需要另外产生一个data.frame再join回来
且要检查多组不同字串规则时变得更为冗长
想问看看有没有更好的写法
ex:
ID 代码一 代码二 ... 代码N
1 O33 O34 O354
2 O331 O354
3 OO33 O345
需要的结果
ID 代码一 代码二 ... 代码N O33开头 O34开头 ...
1 O33 O34 O354 True True
2 O331 O354 True False
3 OO33 O345 False True
[程式范例]:
df <- read.csv( ...... )
require(dplyr)
dftemp <- filter_at(df, vars(starts_with("代码")), any_vars(grepl("^O33", .)))
dftemp$O33开头 <- True
df <- left_join(df, dftemp )
df$O33开头 <- ifelse( is.na(df$O33开头), False, True)
然後为 "O34开头" 再做一次...
--
So stand by your glasses steady,
Here’s good luck to the man in the sky,
Here’s a toast to the dead already,
Three cheers for the next man to die.
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 118.163.222.252 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/R_Language/M.1599453865.A.4D5.html
※ 编辑: daze (118.163.222.252 台湾), 09/07/2020 12:54:34
1F:→ locka: 代码N里面有可能出现033开头的值吗? 09/07 14:06
代码1 ~ 代码N 都可能出现O33开头的值
N的数量不固定
用 filter_at 可以抓 column name 里面有"代码"开头的columns
另外还可能有其他开头不是"代码"的columns
这些columns不可列入检查
2F:→ andrew43: 我觉得先把pattern字串写在一个向量,用for loop就好了 09/07 17:15
3F:→ andrew43: 最後再cbind或join 09/07 17:15
这个code只在代码1比对 O33, 在代码2比对 O34。
在代码2出现O33就不行了。
当然写成巢状 for loop也可以
但是这样实现的话
使用 filter_at 的简洁度与可读性可能更好些
而且在N会变动的情况下
巢状 for loop 会更麻烦些
※ 编辑: daze (114.40.18.158 台湾), 09/07/2020 18:14:49
5F:→ andrew43: 那只要写出合适的pattern还是可以不改结构的。 09/07 18:35
6F:→ andrew43: N会变动应该不麻烦,用ncol()帮忙一下。 09/07 18:36
7F:推 JuanMaestrow: 先gather起来变成只有一个column是代码。mutate用st 09/08 13:42
8F:→ JuanMaestrow: r_detect搭配regex。做完再speead出去就可以了 09/08 13:42
更之前有试着用 reshape2::melt/dcast
行为跟 tidyr::gather/spread 应该很类似
不过有个问题是 melt 後 dataframe 会变大好几倍
比较小的档案可以这麽做,但6G的档案变大10倍,记忆体就爆了
当时的做法是把某些暂时不用的 column 抽掉先把档案瘦身
跑完 melt/dcast 之後再 join 回去
但这样当然比较麻烦,又容易出错
目前处理的档案大小,最大就 10G 左右
刚好落在用 filter_at 不会爆而用 melt 就会爆的状态
filter_at 在效能上对我来说其实刚好够用,能用的参数也算方便
但 filter_at 为了 filter,应该本来就会为每一 row 产生一个逻辑值
我主要的困扰是
希望有现成的类似函式可以直接产生该逻辑值写入新 column
而不必使用 filter_at 生成新 dataframe 再 join 回去
从而可以直接对 patterns list 做 lapply
※ 编辑: daze (114.40.18.158 台湾), 09/08/2020 16:53:57
做两次 lapply 再 cbind,最後把 column name 改掉。
缺点是可能过两个月我就搞不清这段code的作用了。
require(dplyr)
df<- read.csv(...)
pattern.list <- c("^O33", "^O34")
CNames<-colnames(df)
df<-df %>% cbind( lapply( pattern.list, function(y) Reduce("|",
lapply(select(.,starts_with("代码")), function(x) grepl(y, x)))))
colnames(df)<-c(CNames,pattern.list)
※ 编辑: daze (114.40.18.158 台湾), 09/08/2020 21:56:34
这样似乎稍微好一点...
cbind(df, lapply(pattern.list, function(x) df %>% rowwise() %>% summarise(
(sum(grepl( x, across(starts_with("代码"))))>0))))
但这样不是非常有效率
跑 1.7M rows, 在 6个 column中比对1个pattern, 大概要 3~4 分钟
用一开始的 filter_at 实现同样的比对则要花... 3 秒钟。
※ 编辑: daze (114.40.18.158 台湾), 09/08/2020 23:32:04
9F:推 intotherain: 用across+1 很精炼的写法耶 09/09 09:36
10F:推 JuanMaestrow: 我没有叫你melt阿。gather跟spread好很多。你不要 09/09 20:06
11F:→ JuanMaestrow: 自己跑去用melt 09/09 20:06
试着读了一个约 5G 的 CSV 档进来 (12568899 rows x 43 columns)
Rstudio 显示 data.frame 大小是 6259133200 bytes
将 6 个代码栏 gather 成一个
data.frame 大小变成 22696429616 bytes
在工作管理员中, Rsession.exe 的记忆体用量则约 28G
电脑开始 lag, 毕竟实体记忆体只有 32G
把 R 重启後,用 melt 做同样的操作
data.frame 大小变成 22394776472 bytes
电脑仍然开始 lag
所谓 gather 比 melt 好很多,具体是指哪方面?
※ 编辑: daze (36.237.70.94 台湾), 09/09/2020 23:24:29
12F:推 cywhale: 我不知道有无误解题意 如果pattern是有限个 比如 09/11 12:28
13F:→ cywhale: pat=c("033","034","035"); chkhead<-function(x){matrix 09/11 12:34
14F:→ cywhale: pat%in%substr(x,1,3)),ncol=3)} 用apply(df,1,chkhead) 09/11 12:36
15F:→ cywhale: 如果有速度需求可以拆平行做或者分批 如果记忆体不够?.. 09/11 12:38
※ 编辑: daze (114.39.55.253 台湾), 09/13/2020 22:00:23
※ 编辑: daze (114.39.55.253 台湾), 09/14/2020 23:53:52