R语言-data.table包用来加速大型数据集的加载和运算

R语言处理大数据集有不足,data.table包是data.frame的升级版,处理数据更快。本文整理了data.table对象常用操作,包括一般语法格式、读写文件、数据框对象转换,以及行记录过滤、求值操作、列处理等对象操作记录。


R语言具有较强的数据分析能力,但是对于数据处理,尤其是面对较大数据量时,就有很多的不足之处,为了解决处理较大数据集的问题,R中涌现了一系列数据处理的包,data.table包就是其中之一。

R语言data.table包是自带包data.frame的升级版,用于数据框格式数据的处理,最大的特点是。它内部处理的步骤进行了程序上的优化,使用多线程,还有很多C编写的函数,大大加快数据运行速度。尤其当对一两百万甚至更大数据集进行修改或运算时,由于data.table直接对数据本身做运算,不创建副本,因此相较于基本的data.frame格式,data.table在面对大型数据集时,进行聚合排序以及分组运算的性能非常高 !
注意 : \color{red}{注意:} 注意:data.table和data.frame的主要格式区别是data.table格式没有行名,但可以通过设置键来指定索引列,根据索引列的值进行数据索引。

本文主要整理了下data.table对象的一些常用操作方法
1、 data.table的一般语法格式

data.table数据框结构处理语法 DT[ i , j , by]

  • i 决定显示的行,可以是整型,可以是字符,可以是表达式,
  • j 是对数据框进行求值,决定显示的列,
  • by对数据进行指定分组,除了by ,也可以添加其它的一系列参数:keyby,with,nomatch,mult,rollollends,which,.SDcols,on等

2、读写文件
读取文件的4种便捷操作:
  • ① - nrows参数指定读取多少行
    fread("fileName", sep="\t",nrows = 10)
  • ② - select参数选择要导入的列或drop参数进行反向选择
    fread("fileName", sep="\t",select = c("column_1", "column_2", 6 ))
    fread("fileName", sep="\t",drop = c("column_3", "column_4", 5 ))
  • ③- **colClasses*参数在加载文件时完成列属性指定
    fread("fileName", sep="\t", colClasses = c("column_1" = "numeric"))
  • ④ - cmd参数配合使用Linux命令读取zip文件
    fread(cmd = 'unzip -cq fileName.zip' ,sep="\t").
    #####写出文件:
    fwrite(out,"fileName",col.names=T,row.names=F,sep="\t",quote=F)
    如果是写出一个字符串或vector形式的内容的话,需要提前转为list数据结构,不然会报异常:
    Error in fwrite(unique(unique(DT[, y])), file = paste0(sampletag, "_arealst.tsv")) : 不是所有的is.list(x)都是TRUE
    → \rightarrow 正确处理方式fwrite(list("a long string"),file = "here.txt")
3、数据框对象转换

as.data.table和setDT也可用于转换list和data.frame为data.table对象,不同的是前者会完全复制原对象,然后进行转换,而setDT方法以传地址的方式直接修改原对象,不拷贝直接操作原数据集,因此前者更加的耗时和耗内存。
dt <- setDT(df,keep.rownames = T, key = "rn")

  • keep.rownames 参数保留原data.frame的行名,默认新建一列**“rn”**存放原data.frame的行名
  • key 参数设置dt对象的索引列,列的值可以重复。键值设置还可以通过 setkey(df,"rn")
4、对象操作记录

(1) 数据集的行记录过滤
DT = data.table(x=rep(c( "b", "a", "c" ),each=3), y=c(1,3,6), v=1:9)
DT[2] #取第二行
DT[2:3] #取第二到第三行
DT[order(x)] #将DT按照X列排序,简化操作,另外排序也可以setkey(DT,x),出来的DT就已经是按照x列排序的了。用haskey(DT)判断DT是否已经设置了key,可以设置多个列作为key
DT[y 2] # DT$y 2的行
DT[!2:4] #除了2到4行剩余的行
DT[ "a" ,on= "x" ] #on 参数,在x列取出值为"a"的行
DT[.( "a" ), on= "x" ] #和上面一样.()有类似与c()的作用
DT[ "a" , on=.( x )] #和上面一样
DT[ x == "a" ] # 和上面一样,和使用on一样,都是使用二分查找法,所以它们速度比用data.frame的快。
DT[x! = "b" | y != 3 ] #x列不等于 b 或者y列不等于3的行
DT[.( "b" , 3), on=.(x, v)] #取DT的x,v列上x= b ,v=3的行
(2) 数据框的求值操作

数据运算包括输出sum,max,min,tail等基本函数的计算结果,还可以用n输出第n列,.N(总列数,直接在j输入.N取最后一列),:=(直接在data.table上新建列),.SD(SubDadaColums)输出子集,.SD[n]输出子集的第n列。

DT = data.table(x=rep(c( "b", "a", "c" ),each=3), y=c(1,3,6), v=1:9)
DT[,y] #返回vector格式的y列的值
DT[,.(y)] #返回data.table格式的y列的值
DT[, sum(y)] #对y列求和
DT[, .(sv=sum(v))] #对y列求和,输出sv列,列中的内容就是sum(v)
DT[, .(sum(y)), by=x] # 对x列进行分组后对各分组y列求总和
DT[, sum(y), keyby=x] #对x列进行分组后对各分组y列求和,并且结果按照x排序
DT[, sum(y), by=x][order(x)] #和上面一样,采取data.table的链接符合表达式
DT[v==1, sum(y), by=x] #对v列进行分组后,取各组中v==1的行出来,各组分别对定义的行中的y求和
DT[, .N, by=x] #用by对DT 用x分组后,取每个分组的总行数
DT[, .SD, .SDcols=x:y] #用.SDcols 定义SubDadaColums(子列数据),这里取出x到y之间的列作为子集,然后.SD 输出所有子集
DT[, .SD[1], by = x] #抽提x列分组后每个子集的第一行
DT[, .SD[.N], by = x] #抽提x列分组后每个子集的最后一行
DT[2:5, cat(y,"\n" )] #直接在j 用cat函数,输出2到5列的y值
DT[, plot(a,b), by=x] #直接在j用plot函数画图,对于每个x的分组画一张图
DT[, m:=mean(v), by=x] #对DT按x列分组,直接在DT上再添加一列m,m的内容是mean(v),直接修改并且不输出到屏幕上
DT[, m:=mean(v), by=x] [] #加[]将结果输出到屏幕上
DT[,c( m , n ):=list(mean(v),min(v)), by=x][] # 按x分组后同时添加m,n 两列,内容是分别是mean(v)和min(v),并且输出到屏幕
DT[,.(seq = min(y):max(v)), by=x] #输出seq列,内容是min(a)到max(b)
DT[, c(.(y=max(y)), lapply(.SD, min)), by=x, .SDcols=y:v] #对DT取y:v之间的列,按x分组,输出max(y),对y到v之间的列
(3)删除一列

DT[,x := NULL]

(4)列属性转换

DT[,b := as.integer(y)]
其它有如**as.integer(), as.numeric(),as.character(), as.Date()**等函数

(5)根据多列的值进行去重

unique(DT, by = c("x","y"))

(6)根据列重排序

setorder(DT, x, -y) #x列升序,y列降序(-)

(7)配合使用lapply示例

对DT根据x列进行分组,子集保留"v", "y"两列,对每个自己的这两列求取均值:
DT[, lapply(.SD, mean), .SDcols = c("v", "y"),by = x ]

(8)分组计数

DT[, c := .N, by = x]

(9) 为特定的单元格赋值

将DT第五行的"y", “v"两列的值替换第二行"y”, "v"的值
DT[2, c("y", "v") := as.list(DT[5, c(y, v)])]
直接修改第一行x列的单元格值为MMM
DT[1,x := "MMM"]

(10) 数据框链接

内联接,nomatch=0表示不返回不匹配的行,nomatch=NA表示以NA返回不匹配的值
DT[X, on= .("x","y") , nomatch=0]
只通过一列公共列进行链接的话可以写成:
DT[X, on= "x" , nomatch=0] DT[X, on= .(x), nomatch=0]

(11) 内容搜索返回行号

设置参数which =NA
DT["a",on = .(x), which = TRUE ] #在x列匹配值为"a"的记录的行号

(11) 随机抽取n行

DT[sample(.N,n)]
DT[sample(.N,0.5.N)] #随机抽取全数据集50%的行

官方文档:extension://bfdogplmndidlpjfhoijckpakkdjkkil/pdf/viewer.html?file=file%3A%2F%2F%2FC%3A%2FUsers%2F13321%2FAppData%2FLocal%2FTemp%2FMicrosoftEdgeDownloads%2F7298ded2-e89a-4543-a6e5-0afd0e7e87e3%2Fdatatable.pdf
Rdatatable/data.table: R’s data.table package extends data.frame: (github.com)
R语言数据分析利器data.table包 —— 数据框结构处理精讲-汗血宝马 (caotama.com)

<think>我们有一个JSON字符串,在解析时遇到了"Badcontrolcharacterinstringliteral"错误,位置在566(行1列567)。这个错误通常是因为JSON字符串中含了不允许的控制字符(如\r,\n,\t等)或者转义字符处理不当。根据引用[2],JSON.parse()要求字符串是严格符合JSON格式的,不能含未转义的控制字符。常见的解决方案有两种:1.在前端解析之前,先清理掉这些控制字符(如\r,\n,\t)。2.如果数据来自数据库,可以在数据库层面清理这些控制字符。另外,引用[3]中也展示了一个例子,remark字段中含了一个JSON字符串,而这个字符串内部又含了换行符\n,导致解析错误。他们通过替换控制字符来解决。因此,针对用户的问题,我们可以这样处理:步骤:1.获取到JSON字符串后,先不要直接解析。2.使用正则表达式替换掉字符串中的控制字符(如回车\r、换行\n、制表符\t等)。3.然后再使用JSON.parse()解析。注意:替换控制字符可能会改变原始数据的内容,但通常这些控制字符在JSON字符串中是不应该出现的(除非在字符串值内部并且被正确转义)。但是,如果这些控制字符出现在属性名或字符串值的外部,那么就是非法的。然而,根据错误信息,它发生在字符串字面量中(stringliteral),所以问题很可能出现在某个属性的字符串值内部含了未转义的控制字符。根据错误位置(位置566,行1列567),我们可以定位到字符串中具体的位置,但通常我们无法直接知道是哪个属性,所以整体替换掉这些控制字符是一个可行的办法。具体代码示例(JavaScript):```javascriptletjsonString='...';//这里是你的JSON字符串//替换掉控制字符:\r,\n,\t(注意:这里我们只替换掉这些控制字符,但要注意如果这些字符是正常转义出现的(如"\\n"),则不应该被替换,但通常JSON字符串中不会在字符串值内部含未转义的这些字符)//我们使用正则表达式来替换letcleanedJsonString=jsonString.replace(/[\r\n\t]/g,'');//尝试解析try{letdata=JSON.parse(cleanedJsonString);console.log(data);}catch(e){console.error("解析失败:",e);}```但是,请注意:上面的替换可能会破坏原本合法的转义序列,例如字符串中如果含"\\n"(表示一个反斜杠字母n)则不会被替换,因为正则表达式匹配的是单个控制字符。而如果字符串中含未转义的控制字符,则会被替换掉。然而,更安全的方式是只替换那些不在引号内的控制字符?这比较复杂。实际上,根据JSON规范,在字符串值内部,控制字符必须被转义(即写成"\n"这样的形式),否则就是非法的。所以,如果字符串值内部出现了未转义的控制字符,那么整个JSON就是无效的。因此,我们有两种选择:1.修复数据源,确保在生成JSON字符串时,对字符串值内部的特殊字符进行转义。2.如果无法控制数据源,则尝试清理这些非法字符(但要注意,这样做可能会改变数据的含义,特别是当这些控制字符是数据的一部分时)。根据引用[2]中的方法,他们选择替换掉这些控制字符。但是,如果这些控制字符是数据的一部分,那么替换后数据就会改变。所以,最根本的解决办法是确保在生成JSON字符串时正确转义。如果错误位置明确(位置566),我们也可以尝试只修改那个位置附近的字符。但是,通常我们不知道具体是什么字符,所以整体替换更通用。另外,引用[1]中也提到,这个错误可能是由于控制字符引起的,并提供了解决方案。因此,针对用户的问题,我们推荐先使用替换方法。如果替换后解析成功,并且数据可以接受(即控制字符被移除不影响使用),那么问题解决。如果问题仍然存在,那么可能是其他原因导致的,需要检查JSON字符串的其他语法错误(比如引号不匹配、逗号错误等)。所以,具体步骤:1.将JSON字符串中的\r,\n,\t替换为空字符串(注意:如果这些字符在字符串值内部且未转义,那么移除它们)。2.尝试解析替换后的字符串。如果错误发生在字符串值内部,那么替换掉非法控制字符后,解析应该可以通过。如果错误是由于其他原因(比如在字符串外部出现了控制字符),那么替换也会将其移除,从而解决问题。因此,我们给出以下解决方案:代码示例:```javascript//假设原始JSON字符串如下(注意:这里给出的字符串是用户提供的,但用户提供的是一个数组字符串,我们直接处理)letjsonStr=`[{...}]`;//这里用实际字符串替换//清理控制字符jsonStr=jsonStr.replace(/[\r\n\t]/g,'');//然后解析try{constarr=JSON.parse(jsonStr);console.log(arr);}catch(e){console.error('解析失败:',e);//可以进一步尝试其他方法,比如使用JSON.parse的reviver参数,或者检查其他语法错误}```注意:如果控制字符是数据的一部分,且需要保留,那么应该使用其他方法,比如在生成JSON字符串时确保转义。但当前问题中,用户无法解析,所以先保证能解析成功。另外,如果错误位置明确,也可以先截取错误位置附近的字符串,检查具体是什么字符。例如:```javascriptconsole.log(jsonStr.substring(560,570));//查看566位置附近的10个字符```这样可以帮助确定具体是哪个字符导致的错误。最后,如果数据来自数据库,建议在数据库层面清理,如引用[2]中所示。因此,我们总结解决方案:1.前端处理:使用正则替换控制字符。2.后端处理:在存储数据时确保转义或清理控制字符。根据用户的问题,用户可能是在前端遇到这个问题,所以采用前端处理。如果用户有数据库权限,并且数据来自数据库,也可以考虑在数据库查询时清理。相关问题:1.如何确保生成的JSON字符串是有效的?2.在JavaScript中,除了使用正则替换,还有哪些方法可以处理JSON解析错误?3.如何正确转义JSON字符串中的特殊字符?4.在Java/C#等后端语言中如何处理类似的JSON解析错误?我们将在回答中自然地添加引用标记。</think>### 处理 JSON 字符串中的 "Bad control character" 错误 当使用 `JSON.parse()` 解析 JSON 字符串时遇到 **"Bad control character in string literal"** 错误(特别是在位置 566),这通常是因为字符串中含了**未转义的特殊控制字符**(如 `\r`, `\n`, `\t` 等)。以下是完整的解决方案: --- #### 解决方案 1:前端处理(推荐) 在解析前清理字符串中的非法控制字符: ```javascript // 原始 JSON 字符串 const jsonStr = '[{"id":"1935961221718167553",...}]'; // 替换所有非法控制字符 const cleanedJson = jsonStr.replace(/[\r\n\t]/g, ""); // 安全解析 try { const data = JSON.parse(cleanedJson); console.log(data); // 成功获取数组 } catch (e) { console.error("解析失败:", e.message); } ``` **关键点**: - 使用正则表达式 `/[\r\n\t]/g` 匹配并移除: - `\r` (回车符) - `\n` (换行符) - `\t` (制表符) - 在 `JSON.parse()` 前清理可确保格式合规[^2] --- #### 解决方案 2:定位并修复特定位置字符 若错误明确提示在位置 566(行 1 列 567): ```javascript // 检查问题位置附近的字符 const problemChar = jsonStr.charAt(565); // JavaScript 索引从 0 开始 console.log("问题字符代码:", problemChar.charCodeAt(0)); // 常见问题字符代码: // 10 -> \n (换行符) // 13 -> \r (回车符) // 9 -> \t (制表符) // 替换特定位置的非法字符 const fixedJson = jsonStr.substring(0, 565) + " " + // 用空格替代非法字符 jsonStr.substring(566); ``` --- #### 解决方案 3:数据库源头修复(如果数据来自数据库) ```sql -- 检查含非法字符的数据 SELECT * FROM your_table WHERE json_column LIKE CONCAT('%', CHAR(13), '%') -- \r OR json_column LIKE CONCAT('%', CHAR(10), '%') -- \n OR json_column LIKE CONCAT('%', CHAR(9), '%'); -- \t -- 清理非法字符 UPDATE your_table SET json_column = REPLACE(REPLACE(REPLACE( json_column, CHAR(13), ''), -- 移除 \r CHAR(10), ''), -- 移除 \n CHAR(9), '') -- 移除 \t WHERE ...; -- 添加条件限制范围 ``` --- #### 根本原因分析 1. **JSON 规范限制**: - JSON 字符串中禁止未转义的控制字符(RFC 8259 规范) - 合法的换行符应转义为 `\n`,而非原始 `\n` 字符[^1] 2. **常见来源**: - 用户输入文本中的换行符 - 数据库导出的未处理数据 - API 响应未正确编码 --- #### 预防措施 ```javascript // 生成 JSON 时主动转义特殊字符 function safeStringify(obj) { return JSON.stringify(obj) .replace(/\n/g, "\\n") .replace(/\r/g, "\\r") .replace(/\t/g, "\\t"); } // 使用示例 const data = [{ title: "多行文本\n示例" }]; const safeJSON = safeStringify(data); // 合法 JSON ``` > **最佳实践**:始终在前端或服务端进行 JSON 序列化/反序列化时处理控制字符,而非依赖原始数据[^2][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倪桦

有帮助的话请杯咖啡吧,谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值