เทคนิคในการจัดการหน่วยความจำที่มีอยู่ในเซสชัน R


490

เทคนิคใดที่ผู้คนใช้เพื่อจัดการหน่วยความจำที่มีอยู่ของเซสชัน R แบบโต้ตอบ? ฉันใช้ฟังก์ชั่นด้านล่าง [อ้างอิงจากการโพสต์โดย Petr Pikal และ David Hinds ไปยังรายการ r-help ในปี 2004] เพื่อแสดงรายการ (และ / หรือเรียงลำดับ) วัตถุที่ใหญ่ที่สุดและบางครั้งrm()ก็เป็นไปได้ แต่ที่ผ่านมาโซลูชันที่มีประสิทธิภาพมากที่สุดคือ ... ทำงานภายใต้ Linux 64 บิตพร้อมหน่วยความจำที่เพียงพอ

มีเทคนิคดีๆอื่น ๆ ที่ผู้คนต้องการแบ่งปันหรือไม่? กรุณาหนึ่งต่อโพสต์

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.dim)
    names(out) <- c("Type", "Size", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}
# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

หมายเหตุฉันไม่สงสัยเลย แต่มีการใช้งานอย่างไร ฉันค่อนข้างใหม่กับปัญหาหน่วยความจำใน R แต่ฉันพบบางอย่างเมื่อเร็ว ๆ นี้ (นั่นเป็นเหตุผลที่ฉันค้นหาโพสต์นี้ :) - ดังนั้นฉันเพิ่งเริ่มต้นด้วยทั้งหมดนี้ สิ่งนี้ช่วยงานประจำวันของฉันได้อย่างไร
Matt Bannert

4
หากคุณต้องการเห็นวัตถุภายในฟังก์ชั่นคุณต้องใช้: lsos (pos = environment ()) มิฉะนั้นจะแสดงเฉพาะตัวแปรทั่วโลกเท่านั้น ในการเขียนข้อผิดพลาดมาตรฐาน: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn

ทำไมต้องเป็น Linux 64 บิตและไม่ใช่ Windows 64 บิต ตัวเลือกของระบบปฏิบัติการสร้างความแตกต่างที่ไม่สำคัญเมื่อฉันมี RAM 32GB หรือไม่?
Jase

3
@pepsimax: นี้ได้รับการบรรจุในแพคเกจmultilevelPSA แพคเกจที่ถูกออกแบบมาสำหรับอย่างอื่น requireNamespace(multilevelPSA); multilevelPSA::lsos(...)แต่คุณสามารถใช้ฟังก์ชั่นจากที่นั่นโดยไม่ต้องโหลดแพคเกจด้วยการพูดว่า หรือในDmiscแพ็คเกจ (ไม่ใช่ที่ CRAN)
krlmlr

1
ถ้าชุดข้อมูลมีขนาดที่สามารถจัดการได้ฉันมักจะไปที่สตูดิโอ R> สภาพแวดล้อม> มุมมองกริด ที่นี่คุณสามารถดูและเรียงลำดับรายการทั้งหมดในสภาพแวดล้อมปัจจุบันของคุณตามขนาด
kRazzy R

คำตอบ:


197

ให้แน่ใจว่าคุณบันทึกงานของคุณในสคริปต์ที่ทำซ้ำได้ ในบางครั้งให้เปิด R อีกครั้งจากนั้นsource()สคริปต์ของคุณ คุณจะล้างสิ่งที่คุณไม่ได้ใช้อีกต่อไปและเนื่องจากสิทธิประโยชน์เพิ่มเติมจะทดสอบรหัสของคุณ


58
กลยุทธ์ของฉันคือการแบ่งสคริปต์ของฉันตามแนวของ load.R และ do.R โดยที่ load.R อาจใช้เวลาพอสมควรในการโหลดข้อมูลจากไฟล์หรือฐานข้อมูล ข้อมูลนั้น บรรทัดสุดท้ายของ load.R เป็นสิ่งที่จะบันทึกสถานะพื้นที่ทำงาน จากนั้น do.R คือ scratchpad ของฉันโดยที่ฉันสร้างฟังก์ชั่นการวิเคราะห์ของฉันขึ้นมา ฉันโหลดซ้ำ do.R บ่อยครั้ง (โดยมีหรือไม่มีการโหลดสถานะพื้นที่ทำงานจาก load.R ตามต้องการ)
s, Josh Reich

32
นั่นเป็นเทคนิคที่ดี เมื่อไฟล์ที่จะดำเนินการในลำดับที่แน่นอนเช่นเดียวกับที่ผมมักจะนำหน้าพวกเขามีจำนวน: 1-load.r, 2-explore.r, 3-model.r- วิธีการก็เห็นได้ชัดกับคนอื่น ๆ ที่มีบางคำสั่งปัจจุบัน
hadley

4
ฉันไม่สามารถคืนความคิดนี้ให้เพียงพอ ฉันได้สอน R ให้กับคนจำนวนน้อยและนี่เป็นหนึ่งในสิ่งแรกที่ฉันพูด สิ่งนี้ยังใช้กับภาษาใดก็ได้ที่การพัฒนาประกอบด้วย REPL และไฟล์ที่กำลังแก้ไข (เช่น Python) rm (ls = list ()) และ source () ทำงานได้เช่นกัน แต่การเปิดใหม่จะดีกว่า (ล้างแพคเกจด้วย)
วินซ์

53
ความจริงที่ว่าคำตอบที่ได้รับคะแนนสูงสุดเกี่ยวข้องกับการรีสตาร์ท R คือคำวิจารณ์ที่แย่ที่สุดของ R
sds

7
@ MartínBelที่ลบเฉพาะวัตถุที่สร้างขึ้นในสภาพแวดล้อมโลก มันไม่ได้ยกเลิกการโหลดแพ็คเกจหรือวัตถุ S4 หรือสิ่งอื่น ๆ อีกมากมาย
hadley

160

ฉันใช้แพ็คเกจdata.table ด้วย:=โอเปอเรเตอร์คุณสามารถ:

  • เพิ่มคอลัมน์โดยอ้างอิง
  • แก้ไขชุดย่อยของคอลัมน์ที่มีอยู่โดยการอ้างอิงและโดยกลุ่มโดยอ้างอิง
  • ลบคอลัมน์โดยการอ้างอิง

ไม่มีการดำเนินการเหล่านี้คัดลอก (อาจใหญ่) data.tableเลยแม้แต่ครั้งเดียว

  • การรวมยังเร็วมากเพราะdata.tableใช้หน่วยความจำที่ใช้งานได้น้อยกว่ามาก

ลิ้งค์ที่มีความเกี่ยวข้อง :


109

เห็นสิ่งนี้ในโพสต์ Twitter และคิดว่ามันเป็นฟังก์ชั่นที่ยอดเยี่ยมโดย Dirk! ต่อจากคำตอบของ JD Long ฉันจะทำเพื่อการอ่านที่เป็นมิตรกับผู้ใช้:

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.prettysize <- napply(names, function(x) {
                           format(utils::object.size(x), units = "auto") })
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Length/Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()

ซึ่งผลลัพธ์ในบางสิ่งเช่นนี้:

                      Type   Size PrettySize Length/Rows Columns
pca.res                 PCA 790128   771.6 Kb          7      NA
DF               data.frame 271040   264.7 Kb        669      50
factor.AgeGender   factanal  12888    12.6 Kb         12      NA
dates            data.frame   9016     8.8 Kb        669       2
sd.                 numeric   3808     3.7 Kb         51      NA
napply             function   2256     2.2 Kb         NA      NA
lsos               function   1944     1.9 Kb         NA      NA
load               loadings   1768     1.7 Kb         12       2
ind.sup             integer    448  448 bytes        102      NA
x                 character     96   96 bytes          1      NA

หมายเหตุ: ส่วนหลักที่ฉันเพิ่มคือ (ปรับอีกครั้งจากคำตอบของ JD):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })

สามารถเพิ่มฟังก์ชั่นนี้ลงใน dplyr หรือแพ็คเกจสำคัญอื่น ๆ ได้
userJT

1
เป็นที่น่าสังเกตว่า (อย่างน้อยกับ base-3.3.2) capture.outputนั้นไม่จำเป็นอีกต่อไปและobj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })สร้างผลลัพธ์ที่สะอาด ในความเป็นจริงไม่ได้เอามันผลิตคำพูดที่ไม่พึงประสงค์ในการส่งออกเช่นแทน[1] "792.5 Mb" 792.5 Mb
Nutle

ยอดเยี่ยม @Nutle, ผมได้ปรับปรุงรหัสตาม :)
โทนี่ Breyal

ฉันจะเปลี่ยนobj.class <- napply(names, function(x) as.character(class(x))[1])เป็นobj.class <- napply(names, function(x) class(x)[1]) เพราะclassจะคืนค่าเวกเตอร์ของตัวละครเสมอ (base-3.5.0)
DeltaIV

49

ฉันใช้subsetพารามิเตอร์แบบก้าวร้าวโดยเลือกเฉพาะตัวแปรที่จำเป็นเมื่อส่งไฟล์ข้อมูลไปยังdata=อาร์กิวเมนต์ของฟังก์ชันการถดถอย มันทำให้เกิดข้อผิดพลาดบางอย่างถ้าฉันลืมเพิ่มตัวแปรให้กับสูตรและselect=เวกเตอร์ แต่ก็ยังประหยัดเวลาได้มากเนื่องจากการคัดลอกวัตถุลดลงและลดขนาดหน่วยความจำลงอย่างมาก ว่าฉันมี 4 ล้านบันทึกที่มีตัวแปร 110 (และฉันทำ) ตัวอย่าง:

# library(rms); library(Hmisc) for the cph,and rcs functions
Mayo.PrCr.rbc.mdl <- 
cph(formula = Surv(surv.yr, death) ~ age + Sex + nsmkr + rcs(Mayo, 4) + 
                                     rcs(PrCr.rat, 3) +  rbc.cat * Sex, 
     data = subset(set1HLI,  gdlab2 & HIVfinal == "Negative", 
                           select = c("surv.yr", "death", "PrCr.rat", "Mayo", 
                                      "age", "Sex", "nsmkr", "rbc.cat")
   )            )

โดยวิธีการตั้งค่าบริบทและกลยุทธ์: gdlab2ตัวแปรเป็นเวกเตอร์เชิงตรรกะที่สร้างขึ้นสำหรับวิชาในชุดข้อมูลที่มีค่าปกติหรือเกือบทั้งหมดสำหรับการทดสอบในห้องปฏิบัติการและHIVfinalเป็นเวกเตอร์ตัวอักษรที่สรุปการทดสอบเบื้องต้นและยืนยันการติดเชื้อ HIV .


48

ฉันชอบสคริปต์. ls.objects () ของเดิร์ค แต่ฉันก็ยังเหลื่อมกันเพื่อนับจำนวนตัวอักษรในคอลัมน์ขนาด ดังนั้นฉันจึงแฮ็กที่น่าเกลียดเพื่อนำเสนอด้วยการจัดรูปแบบสวยขนาด:

.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.prettysize <- sapply(obj.size, function(r) prettyNum(r, big.mark = ",") )
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size,obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
        out <- out[c("Type", "PrettySize", "Rows", "Columns")]
        names(out) <- c("Type", "Size", "Rows", "Columns")
    if (head)
        out <- head(out, n)
    out
}

34

นั่นเป็นเคล็ดลับที่ดี

ข้อเสนอแนะอีกอย่างหนึ่งคือการใช้วัตถุที่มีประสิทธิภาพของหน่วยความจำทุกที่ที่เป็นไปได้ตัวอย่างเช่นใช้เมทริกซ์แทน data.frame

สิ่งนี้ไม่ได้จัดการกับการจัดการหน่วยความจำอย่างแท้จริง แต่ฟังก์ชั่นสำคัญอย่างหนึ่งที่ไม่รู้จักกันอย่างกว้างขวางคือ memory.limit () คุณสามารถเพิ่มค่าเริ่มต้นโดยใช้คำสั่งนี้ memory.limit (size = 2500) โดยที่ขนาดเป็น MB ดังที่เดิร์กกล่าวคุณจะต้องใช้ 64- บิตเพื่อใช้ประโยชน์จากสิ่งนี้อย่างแท้จริง


25
นี่ใช้ได้เฉพาะกับ Windows ใช่หรือไม่
Christopher DuBois

4
> memory.limit () [1] Inf ข้อความเตือน: 'memory.limit ()' เป็นเฉพาะ Windows
LJT

การใช้ tibble แทน data.frame ช่วยเราได้ดียิ่งขึ้นในการประหยัดหน่วยความจำหรือไม่?

32

ฉันค่อนข้างชอบฟังก์ชั่นวัตถุที่ได้รับการปรับปรุงซึ่งพัฒนาโดยเดิร์ค แม้ว่าเวลาส่วนใหญ่ผลลัพธ์พื้นฐานที่มีชื่อและขนาดของวัตถุก็เพียงพอสำหรับฉันแล้ว นี่คือฟังก์ชั่นที่ง่ายกว่าโดยมีวัตถุประสงค์ที่คล้ายกัน การใช้หน่วยความจำสามารถจัดเรียงตามตัวอักษรหรือขนาดสามารถ จำกัด จำนวนวัตถุได้และสามารถสั่งขึ้นหรือลง นอกจากนี้ฉันมักจะทำงานกับข้อมูลที่มี 1GB + ดังนั้นฟังก์ชั่นจะเปลี่ยนหน่วยตาม

showMemoryUse <- function(sort="size", decreasing=FALSE, limit) {

  objectList <- ls(parent.frame())

  oneKB <- 1024
  oneMB <- 1048576
  oneGB <- 1073741824

  memoryUse <- sapply(objectList, function(x) as.numeric(object.size(eval(parse(text=x)))))

  memListing <- sapply(memoryUse, function(size) {
        if (size >= oneGB) return(paste(round(size/oneGB,2), "GB"))
        else if (size >= oneMB) return(paste(round(size/oneMB,2), "MB"))
        else if (size >= oneKB) return(paste(round(size/oneKB,2), "kB"))
        else return(paste(size, "bytes"))
      })

  memListing <- data.frame(objectName=names(memListing),memorySize=memListing,row.names=NULL)

  if (sort=="alphabetical") memListing <- memListing[order(memListing$objectName,decreasing=decreasing),] 
  else memListing <- memListing[order(memoryUse,decreasing=decreasing),] #will run if sort not specified or "size"

  if(!missing(limit)) memListing <- memListing[1:limit,]

  print(memListing, row.names=FALSE)
  return(invisible(memListing))
}

และนี่คือตัวอย่างผลลัพธ์:

> showMemoryUse(decreasing=TRUE, limit=5)
      objectName memorySize
       coherData  713.75 MB
 spec.pgram_mine  149.63 kB
       stoch.reg  145.88 kB
      describeBy    82.5 kB
      lmBandpass   68.41 kB

30

ฉันไม่เคยบันทึกเวิร์กสเปซ R ฉันใช้สคริปต์นำเข้าและสคริปต์ข้อมูลและส่งออกวัตถุข้อมูลขนาดใหญ่โดยเฉพาะที่ฉันไม่ต้องการสร้างไฟล์บ่อยครั้ง วิธีนี้ฉันเริ่มต้นด้วยพื้นที่ทำงานใหม่เสมอและไม่จำเป็นต้องล้างวัตถุขนาดใหญ่ นั่นเป็นฟังก์ชั่นที่ดีมาก ๆ


30

น่าเสียดายที่ฉันไม่มีเวลาทดสอบอย่างกว้างขวาง แต่นี่เป็นเคล็ดลับความจำที่ฉันไม่เคยเห็นมาก่อน สำหรับฉันแล้วหน่วยความจำที่ต้องการลดลงมากกว่า 50% เมื่อคุณอ่านข้อมูลใน R ด้วยเช่น read.csv พวกเขาต้องการหน่วยความจำจำนวนหนึ่ง หลังจากนี้คุณสามารถบันทึกได้save("Destinationfile",list=ls()) ในครั้งถัดไปที่คุณเปิด R คุณสามารถใช้load("Destinationfile") ตอนนี้การใช้หน่วยความจำอาจลดลง มันจะดีถ้าใครสามารถยืนยันได้ว่าสิ่งนี้จะให้ผลลัพธ์ที่คล้ายกันกับชุดข้อมูลที่แตกต่างกัน


4
ใช่ฉันเคยมีประสบการณ์แบบเดียวกัน การใช้หน่วยความจำลดลงถึง 30% ในกรณีของฉัน ใช้หน่วยความจำ 1.5GB บันทึกไปที่. Data (~ 30MB) เซสชันใหม่หลังจากโหลด. RData ใช้หน่วยความจำน้อยกว่า 500MB
f3lix

ฉันพยายามกับ 2 ชุดข้อมูล (100MB และ 2.7GB) โหลดลง data.table โดยใช้freadแล้วบันทึกลงใน. Data ไฟล์ RData นั้นมีขนาดเล็กกว่าประมาณ 70% แต่หลังจากโหลดซ้ำหน่วยความจำที่ใช้นั้นเหมือนกันทุกประการ หวังว่าเคล็ดลับนี้จะช่วยลดความทรงจำของรอยเท้า ... ฉันทำบางสิ่งหายไปหรือไม่
NoviceProg

@NoviceProg ฉันไม่คิดว่าคุณจะพลาดบางสิ่งบางอย่าง แต่มันเป็นกลอุบายฉันเดาว่ามันจะไม่ทำงานในทุกสถานการณ์ ในกรณีของฉันหน่วยความจำหลังจากโหลดซ้ำลดลงจริงตามที่อธิบายไว้
Dennis Jaheruddin

6
@NoviceProg สองสิ่ง ก่อนอื่น fread ลัทธิความเชื่อของ data.table น่าจะมีประสิทธิภาพในการโหลดไฟล์มากกว่า read.csv ประการที่สองการประหยัดหน่วยความจำที่ผู้คนกำลังสังเกตที่นี่ส่วนใหญ่เกี่ยวข้องกับขนาดหน่วยความจำของกระบวนการ R (ซึ่งขยายเพื่อเก็บวัตถุและถอนเมื่อมีการรวบรวมขยะ) อย่างไรก็ตามการรวบรวมขยะไม่ได้ปล่อย RAM ทั้งหมดกลับคืนสู่ระบบปฏิบัติการเสมอไป การหยุดเซสชัน R และโหลดรายการจากที่เก็บไว้จะปล่อย RAM มากที่สุดเท่าที่จะเป็นไปได้ ... แต่ถ้าค่าโสหุ้ยมีขนาดเล็กเริ่มต้นด้วย ... ไม่ได้รับ
russellpierce

27

เพื่อแสดงให้เห็นถึงกลยุทธ์ทั่วไปของการรีสตาร์ทบ่อยครั้งเราสามารถใช้littlerซึ่งช่วยให้เราสามารถเรียกใช้นิพจน์อย่างง่าย ๆ ได้โดยตรงจากบรรทัดคำสั่ง นี่คือตัวอย่างบางครั้งฉันใช้เวลา BLAS ที่แตกต่างกันสำหรับ crossprod อย่างง่าย

 r -e'N<-3*10^3; M<-matrix(rnorm(N*N),ncol=N); print(system.time(crossprod(M)))'

ในทำนองเดียวกัน

 r -lMatrix -e'example(spMatrix)'

โหลดแพ็คเกจ Matrix (ผ่านสวิตช์ --packages | -l) และเรียกใช้ตัวอย่างของฟังก์ชัน spMatrix เนื่องจาก r เริ่มต้น 'สด' เสมอวิธีนี้จึงเป็นการทดสอบที่ดีระหว่างการพัฒนาบรรจุภัณฑ์

สุดท้าย แต่ไม่ท้ายสุดก็ใช้งานได้ดีสำหรับโหมดแบตช์อัตโนมัติในสคริปต์โดยใช้ส่วนหัว '#! / usr / bin / r' Rscript เป็นทางเลือกที่ littler ไม่พร้อมใช้งาน (เช่นบน Windows)


23

สำหรับทั้งความเร็วและวัตถุประสงค์ของหน่วยความจำเมื่อสร้างกรอบข้อมูลขนาดใหญ่ผ่านขั้นตอนที่ซับซ้อนหลายขั้นตอนฉันจะล้างข้อมูลเป็นระยะ (ชุดข้อมูลกำลังดำเนินการสร้าง) ลงดิสก์เป็นระยะ ๆ ผนวกกับสิ่งใดก็ตามที่มาก่อนแล้วรีสตาร์ท . วิธีนี้ขั้นตอนกลางจะทำงานกับเฟรมข้อมูลขนาดเล็กเท่านั้น (ซึ่งก็ดีเช่นrbindช้าลงอย่างมากกับวัตถุขนาดใหญ่) ชุดข้อมูลทั้งหมดสามารถอ่านได้ในตอนท้ายของกระบวนการเมื่อวัตถุกลางทั้งหมดถูกลบออก

dfinal <- NULL
first <- TRUE
tempfile <- "dfinal_temp.csv"
for( i in bigloop ) {
    if( !i %% 10000 ) { 
        print( i, "; flushing to disk..." )
        write.table( dfinal, file=tempfile, append=!first, col.names=first )
        first <- FALSE
        dfinal <- NULL   # nuke it
    }

    # ... complex operations here that add data to 'dfinal' data frame  
}
print( "Loop done; flushing to disk and re-reading entire data set..." )
write.table( dfinal, file=tempfile, append=TRUE, col.names=FALSE )
dfinal <- read.table( tempfile )

17

เพียงสังเกตว่าdata.tableแพคเกจนั้นtables()ดูเหมือนจะเป็นการทดแทนที่ดีสำหรับ.ls.objects()ฟังก์ชั่นที่กำหนดเองของ Dirk (รายละเอียดในคำตอบก่อนหน้านี้) แม้ว่าจะเป็นเพียง data.frames / tables และไม่ใช่เมทริกซ์, อาร์เรย์, รายการ


นี่ไม่ได้แสดงรายการข้อมูลใด ๆ เฟรมจึงไม่ดีนัก
userJT

16
  1. ฉันโชคดีและชุดข้อมูลขนาดใหญ่ของฉันได้รับการบันทึกโดยเครื่องมือใน "ชิ้นส่วน" (ชุดย่อย) ที่มีขนาดประมาณ 100 MB (ไบนารี 32 บิต) ดังนั้นฉันสามารถทำขั้นตอนการประมวลผลล่วงหน้า (การลบส่วนที่ไม่เป็นทางการการสุ่มตัวอย่าง) ตามลำดับก่อนที่จะหลอมรวมชุดข้อมูล

  2. การเรียกgc ()"ด้วยมือ" สามารถช่วยได้ถ้าขนาดของข้อมูลใกล้เคียงกับหน่วยความจำที่มีอยู่

  3. บางครั้งอัลกอริทึมที่แตกต่างต้องการหน่วยความจำน้อยกว่ามาก
    บางครั้งมีการแลกเปลี่ยนระหว่าง vectorization และการใช้หน่วยความจำ
    Compare: split& lapplyvs. a forloop

  4. เพื่อการวิเคราะห์ข้อมูลที่ง่ายและรวดเร็วฉันมักจะทำงานก่อนด้วยชุดย่อยแบบสุ่มขนาดเล็ก ( sample ()) ของข้อมูล เมื่อสคริปต์การวิเคราะห์ข้อมูล / .Rnw เสร็จสิ้นการวิเคราะห์ข้อมูลรหัสและข้อมูลที่สมบูรณ์ไปที่เซิร์ฟเวอร์การคำนวณสำหรับการคำนวณข้ามคืน / สุดสัปดาห์ / ...


11

การใช้สภาพแวดล้อมแทนรายการเพื่อจัดการกับคอลเลกชันของวัตถุที่ครอบครองหน่วยความจำการทำงานจำนวนมาก

เหตุผล: ทุกครั้งที่มีการแก้ไของค์ประกอบของlistโครงสร้างรายการทั้งหมดจะถูกทำซ้ำชั่วคราว สิ่งนี้จะกลายเป็นปัญหาหากความต้องการพื้นที่เก็บข้อมูลของรายการมีอยู่ประมาณครึ่งหนึ่งของหน่วยความจำที่ใช้งานได้เนื่องจากข้อมูลจะต้องถูกเปลี่ยนเป็นฮาร์ดไดรฟ์ช้า ในทางกลับกันสภาพแวดล้อมไม่อยู่ภายใต้พฤติกรรมนี้และพวกเขาสามารถได้รับการปฏิบัติคล้ายกับรายการ

นี่คือตัวอย่าง:

get.data <- function(x)
{
  # get some data based on x
  return(paste("data from",x))
}

collect.data <- function(i,x,env)
{
  # get some data
  data <- get.data(x[[i]])
  # store data into environment
  element.name <- paste("V",i,sep="")
  env[[element.name]] <- data
  return(NULL)  
}

better.list <- new.env()
filenames <- c("file1","file2","file3")
lapply(seq_along(filenames),collect.data,x=filenames,env=better.list)

# read/write access
print(better.list[["V1"]])
better.list[["V2"]] <- "testdata"
# number of list elements
length(ls(better.list))

เมื่อใช้ร่วมกับโครงสร้างเช่นbig.matrixหรือdata.tableอนุญาตให้แก้ไขเนื้อหาในสถานที่สามารถใช้งานหน่วยความจำได้อย่างมีประสิทธิภาพ


6
สิ่งนี้ไม่เป็นความจริงอีกต่อไป: จากการใช้R ขั้นสูงของ Hadley "การเปลี่ยนแปลงเป็น R 3.1.0 ทำให้การใช้งานนี้ [ของสภาพแวดล้อม] มีความสำคัญน้อยลงอย่างมากเนื่องจากการแก้ไขรายการจะไม่ทำสำเนาลึกอีกต่อไป"
petrelharp

8

llฟังก์ชั่นในgDataแพคเกจที่สามารถแสดงการใช้งานหน่วยความจำของแต่ละวัตถุเช่นกัน

gdata::ll(unit='MB')

ไม่ได้อยู่ในระบบของฉัน: รุ่น R 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64 บิต), gdata_2.13.3, gtools_3.4.1
krlmlr

คุณถูกต้องแล้วที่ฉันทดสอบเมื่อมีโอกาสสั่ง!
user1436187

1
โปรดแก้ไขฟังก์ชั่นการใช้ Gb, Mb
userJT

7

หากคุณต้องการหลีกเลี่ยงการรั่วไหลจริงๆคุณควรหลีกเลี่ยงการสร้างวัตถุขนาดใหญ่ในสภาพแวดล้อมโลก

สิ่งที่ฉันมักจะทำคือการมีฟังก์ชั่นที่ทำงานและส่งคืนNULLข้อมูลทั้งหมดจะถูกอ่านและจัดการในฟังก์ชั่นนี้หรืออื่น ๆ ที่มันเรียก


7

ด้วย RAM ขนาด 4GB เท่านั้น (ใช้งาน Windows 10 ดังนั้นให้ทำอย่างนั้นประมาณ 2 หรือมากกว่าที่เป็นจริง 1GB) ฉันต้องระวังอย่างมากเกี่ยวกับการจัดสรร

ฉันใช้ data.table เกือบเฉพาะ

ฟังก์ชั่น 'fread' ช่วยให้คุณสามารถย่อยข้อมูลตามชื่อฟิลด์เมื่อนำเข้า นำเข้าเฉพาะเขตข้อมูลที่จำเป็นต้องมีเพื่อเริ่มต้นด้วย หากคุณใช้การอ่านฐาน R ให้ลบคอลัมน์ปลอมโดยทันทีหลังจากนำเข้า

ในฐานะที่เป็น42-แนะนำให้เป็นไปได้ที่ฉันจะเซตย่อยภายในคอลัมน์ทันทีหลังจากนำเข้าข้อมูล

ฉันมักจะ rm () วัตถุจากสภาพแวดล้อมทันทีที่พวกเขาไม่ต้องการอีกต่อไปเช่นในบรรทัดถัดไปหลังจากที่ใช้พวกเขาเพื่อเซตย่อยอย่างอื่นและเรียก gc ()

'fread' และ 'fwrite' จาก data.table สามารถเร็วมากโดยการเปรียบเทียบกับการอ่านและเขียนฐาน R

ตามที่kpierce8แนะนำฉันเกือบจะเขียนทุกอย่างออกจากสภาพแวดล้อมและปลดปล่อยมันกลับเข้าไปใหม่แม้จะมีไฟล์เล็ก ๆ เป็นพัน / แสนไฟล์ก็ตาม สิ่งนี้ไม่เพียง แต่ทำให้สภาพแวดล้อม 'สะอาด' และทำให้การจัดสรรหน่วยความจำเหลือน้อย แต่อาจเนื่องมาจากการขาด RAM อย่างรุนแรง R มีแนวโน้มที่จะล้มเหลวบนคอมพิวเตอร์ของฉันบ่อยๆ บ่อยมาก การมีข้อมูลสำรองไว้ในตัวไดรฟ์ขณะที่โค้ดดำเนินการผ่านขั้นตอนต่าง ๆ หมายความว่าฉันไม่จำเป็นต้องเริ่มต้นทันทีตั้งแต่เกิดปัญหา

ในปี 2560 ฉันคิดว่า SSD ที่เร็วที่สุดนั้นทำงานได้สองสาม GB ต่อวินาทีผ่านพอร์ต M2 ฉันมี Kingston V300 พื้นฐานขนาด 50GB (550MB / s) SSD ที่ฉันใช้เป็นดิสก์หลัก (มี Windows และ R อยู่) ฉันเก็บข้อมูลจำนวนมากทั้งหมดไว้ในแผ่นเสียง WD ราคาถูก 500GB ฉันย้ายชุดข้อมูลไปยัง SSD เมื่อฉันเริ่มทำงานกับชุดข้อมูลเหล่านั้น สิ่งนี้รวมกับ 'fread'ing และ' fwrite'ing ทุกอย่างได้ผลดีมาก ฉันได้ลองใช้ 'ff' แต่ชอบใช้ตัวเก่ากว่า ความเร็วในการอ่าน / เขียน 4K สามารถสร้างปัญหาได้ สำรองข้อมูลหนึ่งในสี่ล้านไฟล์ 1k (มูลค่า 250MBs) จาก SSD ไปยังแผ่นเสียงอาจใช้เวลาหลายชั่วโมง เท่าที่ฉันทราบยังไม่มีแพ็คเกจ R ที่สามารถใช้งานได้โดยอัตโนมัติเพิ่มประสิทธิภาพของกระบวนการ 'การแยก' ดูที่ RAM ที่ผู้ใช้มี ทดสอบความเร็วในการอ่าน / เขียนของ RAM / ไดรฟ์ทั้งหมดที่เชื่อมต่อแล้วแนะนำโปรโตคอล 'chunkification' ที่ดีที่สุด สิ่งนี้สามารถสร้างการปรับปรุงเวิร์กโฟลว์ที่สำคัญ / การปรับแต่งทรัพยากรให้เหมาะสม เช่นแบ่งเป็น ... MB สำหรับ ram -> แยกเป็น ... MB สำหรับ SSD -> แยกเป็น ... MB บนแผ่นเสียง -> แยกเป็น ... MB บนเทป มันสามารถสุ่มตัวอย่างชุดข้อมูลล่วงหน้าเพื่อให้แท่งเกจวัดที่สมจริงยิ่งขึ้นใช้งานได้

ปัญหามากมายที่ฉันได้ทำใน R นั้นเกี่ยวข้องกับการรวมคู่และการเรียงสับเปลี่ยน, triples และอื่น ๆ , ซึ่งทำให้มีข้อ จำกัด ของ RAM มากกว่าข้อ จำกัด เนื่องจากพวกเขามักจะขยายอย่างน้อยชี้แจงแทนในบางจุด สิ่งนี้ทำให้ฉันมุ่งเน้นความสนใจไปที่คุณภาพมากกว่าปริมาณข้อมูลที่จะเริ่มต้นแทนที่จะพยายามล้างข้อมูลในภายหลังและลำดับการดำเนินการในการเตรียมข้อมูลเริ่มต้น (เริ่มต้นด้วย การดำเนินการที่ง่ายที่สุดและเพิ่มความซับซ้อน); เช่นชุดย่อยจากนั้นผสาน / เข้าร่วมจากนั้นรวมชุดรูปแบบ / พีชคณิต

ดูเหมือนจะมีประโยชน์บางอย่างในการใช้การอ่านและเขียนฐาน R ในบางกรณี ตัวอย่างเช่นการตรวจจับข้อผิดพลาดภายใน 'fread' นั้นดีมากมันอาจเป็นเรื่องยากที่จะพยายามดึงข้อมูลที่ยุ่งเหยิงเข้าสู่ R เพื่อเริ่มต้นทำความสะอาด ดูเหมือนว่า Base R จะง่ายขึ้นมากถ้าคุณใช้ Linux ดูเหมือนว่า Base R จะทำงานได้ดีใน Linux Windows 10 ใช้พื้นที่ดิสก์ประมาณ 20GB ในขณะที่ Ubuntu ต้องการเพียงไม่กี่ GB RAM ที่จำเป็นสำหรับ Ubuntu นั้นต่ำกว่าเล็กน้อย แต่ฉันสังเกตเห็นคำเตือนและข้อผิดพลาดจำนวนมากเมื่อติดตั้งแพ็คเกจของบุคคลที่สามใน (L) Ubuntu ฉันจะไม่แนะนำให้ล่องลอยห่างจาก (L) Ubuntu หรือการกระจายสต็อคอื่น ๆ ด้วย Linux เนื่องจากคุณสามารถสูญเสียความเข้ากันได้โดยรวมมากมันทำให้กระบวนการเกือบไม่มีจุดหมาย (ฉันคิดว่า 'unity' เนื่องจากถูกยกเลิกใน Ubuntu ตั้งแต่ปี 2560 )

หวังว่าสิ่งเหล่านี้อาจช่วยผู้อื่นได้


5

สิ่งนี้ไม่เพิ่มอะไรข้างต้น แต่เขียนในสไตล์ที่เรียบง่ายและมีความเห็นอย่างหนักที่ฉันชอบ มันให้ตารางกับวัตถุที่สั่งขนาด แต่ไม่มีรายละเอียดบางอย่างที่ระบุในตัวอย่างด้านบน:

#Find the objects       
MemoryObjects = ls()    
#Create an array
MemoryAssessmentTable=array(NA,dim=c(length(MemoryObjects),2))
#Name the columns
colnames(MemoryAssessmentTable)=c("object","bytes")
#Define the first column as the objects
MemoryAssessmentTable[,1]=MemoryObjects
#Define a function to determine size        
MemoryAssessmentFunction=function(x){object.size(get(x))}
#Apply the function to the objects
MemoryAssessmentTable[,2]=t(t(sapply(MemoryAssessmentTable[,1],MemoryAssessmentFunction)))
#Produce a table with the largest objects first
noquote(MemoryAssessmentTable[rev(order(as.numeric(MemoryAssessmentTable[,2]))),])

5

นี่เป็นคำตอบที่ใหม่กว่าสำหรับคำถามเก่าที่ยอดเยี่ยมนี้ จาก Hadley's Advanced R:

install.packages("pryr")

library(pryr)

object_size(1:10)
## 88 B

object_size(mean)
## 832 B

object_size(mtcars)
## 6.74 kB

( http://adv-r.had.co.nz/memory.html )


3

ถ้าคุณกำลังทำงานบนลินุกซ์และต้องการใช้หลายขั้นตอนและจะต้องทำเพียงอ่านดำเนินการอย่างใดอย่างหนึ่งหรือมากกว่าวัตถุขนาดใหญ่ใช้แทนmakeForkCluster makePSOCKclusterสิ่งนี้จะช่วยคุณประหยัดเวลาในการส่งวัตถุขนาดใหญ่ไปยังกระบวนการอื่น


2

ฉันซาบซึ้งกับคำตอบข้างบน @hadley และ @Dirk ที่แนะนำให้ปิด R และออกsourceและใช้ command line ฉันคิดวิธีแก้ปัญหาที่ได้ผลดีสำหรับฉัน ฉันต้องจัดการกับสเป็คตรัมจำนวนมากแต่ละอันมีหน่วยความจำประมาณ 20 Mb ดังนั้นฉันจึงใช้สคริปต์ R สองตัวดังนี้:

เสื้อคลุมแรก:

#!/usr/bin/Rscript --vanilla --default-packages=utils

for(l in 1:length(fdir)) {

   for(k in 1:length(fds)) {
     system(paste("Rscript runConsensus.r", l, k))
   }
}

ด้วยสคริปต์นี้ฉันจะควบคุมสิ่งที่สคริปต์หลักของฉันทำrunConsensus.rและฉันเขียนคำตอบข้อมูลสำหรับผลลัพธ์ ด้วยวิธีนี้ทุกครั้งที่ wrapper เรียกสคริปต์ดูเหมือนว่า R จะถูกเปิดใหม่และหน่วยความจำจะถูกปล่อยให้เป็นอิสระ

หวังว่ามันจะช่วย


2

เช่นเดียวกับเทคนิคการจัดการหน่วยความจำทั่วไปที่ให้ไว้ในคำตอบข้างต้นฉันพยายามลดขนาดของวัตถุเท่าที่จะทำได้ ตัวอย่างเช่นฉันทำงานกับเมทริกซ์ที่มีขนาดใหญ่มาก แต่กระจัดกระจายมากนั่นก็คือเมทริกซ์ที่ค่าส่วนใหญ่เป็นศูนย์ การใช้แพ็คเกจ 'Matrix' (การใช้อักษรตัวพิมพ์ใหญ่ที่สำคัญ) ฉันสามารถลดขนาดวัตถุเฉลี่ยของฉันจาก ~ 2GB ถึง ~ 200MB ได้ง่ายๆเช่นเดียวกับ:

my.matrix <- Matrix(my.matrix)

แพ็คเกจ Matrix มีรูปแบบข้อมูลที่สามารถใช้งานได้เหมือนเมทริกซ์ทั่วไป (ไม่จำเป็นต้องเปลี่ยนรหัสอื่น ๆ ของคุณ) แต่สามารถจัดเก็บข้อมูลที่กระจัดกระจายได้อย่างมีประสิทธิภาพมากขึ้นไม่ว่าจะโหลดลงในหน่วยความจำหรือบันทึกลงดิสก์

นอกจากนี้ไฟล์ดิบที่เราได้รับอยู่ในรูปแบบ 'ยาว' x, y, z, iซึ่งแต่ละจุดข้อมูลมีตัวแปร มีประสิทธิภาพมากขึ้นในการแปลงข้อมูลให้เป็นx * y * zอาร์เรย์ขนาดที่มีตัวแปรเท่านั้นiอาร์เรย์มิติกับตัวแปรเท่านั้น

รู้ข้อมูลของคุณและใช้สามัญสำนึกเล็กน้อย


2

เคล็ดลับสำหรับการจัดการกับวัตถุที่ต้องการการคำนวณระดับกลางอย่างหนัก:เมื่อใช้วัตถุที่ต้องใช้การคำนวณจำนวนมากและขั้นตอนกลางในการสร้างฉันมักจะพบว่ามีประโยชน์ในการเขียนกลุ่มของรหัสด้วยฟังก์ชั่นเพื่อสร้างวัตถุแล้วแยกชิ้น ของรหัสที่ให้ฉันเลือกที่จะสร้างและบันทึกวัตถุเป็นrmdไฟล์หรือโหลดภายนอกจากrmdไฟล์ที่ฉันได้บันทึกไว้ก่อนหน้านี้ นี่เป็นเรื่องง่ายโดยเฉพาะอย่างยิ่งในการR Markdownใช้โครงสร้างโค้ดขนาดต่อไปนี้

```{r Create OBJECT}

COMPLICATED.FUNCTION <- function(...) { Do heavy calculations needing lots of memory;
                                        Output OBJECT; }

```
```{r Generate or load OBJECT}

LOAD <- TRUE;
#NOTE: Set LOAD to TRUE if you want to load saved file
#NOTE: Set LOAD to FALSE if you want to generate and save

if(LOAD == TRUE) { OBJECT <- readRDS(file = 'MySavedObject.rds'); } else
                 { OBJECT <- COMPLICATED.FUNCTION(x, y, z);
                             saveRDS(file = 'MySavedObject.rds', object = OBJECT); }

```

ด้วยโครงสร้างรหัสนี้สิ่งที่ฉันต้องทำก็คือการเปลี่ยนแปลงLOADขึ้นอยู่กับว่าฉันต้องการสร้างและบันทึกวัตถุหรือโหลดโดยตรงจากไฟล์ที่บันทึกไว้ที่มีอยู่ (แน่นอนฉันต้องสร้างและบันทึกในครั้งแรก แต่หลังจากนี้ฉันมีตัวเลือกในการโหลด) การตั้งค่าLOAD = TRUEข้ามการใช้งานที่ซับซ้อนของฉันและหลีกเลี่ยงการคำนวณหนักทั้งหมดในนั้น วิธีนี้ยังต้องการหน่วยความจำเพียงพอในการจัดเก็บออบเจกต์ที่น่าสนใจ แต่จะช่วยให้คุณไม่ต้องคำนวณทุกครั้งที่คุณเรียกใช้รหัสของคุณ สำหรับวัตถุที่ต้องใช้การคำนวณจำนวนมากอย่างหนักของขั้นตอนกลาง (เช่นสำหรับการคำนวณที่เกี่ยวข้องกับลูปในอาร์เรย์ขนาดใหญ่) สิ่งนี้สามารถประหยัดเวลาและการคำนวณได้อย่างมาก


1

วิ่ง

for (i in 1:10) 
    gc(reset = T)

เมื่อเวลาผ่านไปยังช่วยให้ R เพื่อฟรีหน่วยความจำที่ไม่ได้ใช้ แต่ยังไม่ออก


ไม่อะไรคือสิ่งที่forห่วงทำที่นี่? ไม่มีiการgcโทร
Umaomamaomao

@qqq มีเพียงเพื่อหลีกเลี่ยงการคัดลอกวางgc(reset = T)เก้าครั้ง
Marcelo Ventura

14
แต่ทำไมคุณถึงเรียกใช้ 9 ครั้ง (อยากรู้อยากเห็นไม่สำคัญ)
Umaomamaomao

1

คุณสามารถได้รับประโยชน์จากการใช้ knitr และวางสคริปต์ของคุณใน Rmd chuncks

ฉันมักจะแบ่งรหัสในกลุ่มที่แตกต่างกันและเลือกที่จะบันทึกจุดตรวจแคชหรือไฟล์ RDS และ

ตรงนั้นคุณสามารถตั้งค่ากลุ่มที่จะบันทึกเป็น "แคช" หรือคุณสามารถตัดสินใจที่จะเรียกใช้หรือไม่รับเฉพาะก้อน ด้วยวิธีนี้ในการใช้งานครั้งแรกคุณสามารถประมวลผลเฉพาะ "ส่วนที่ 1" การดำเนินการอื่นที่คุณสามารถเลือกได้เฉพาะ "ส่วนที่ 2" ฯลฯ

ตัวอย่าง:

part1
```{r corpus, warning=FALSE, cache=TRUE, message=FALSE, eval=TRUE}
corpusTw <- corpus(twitter)  # build the corpus
```
part2
```{r trigrams, warning=FALSE, cache=TRUE, message=FALSE, eval=FALSE}
dfmTw <- dfm(corpusTw, verbose=TRUE, removeTwitter=TRUE, ngrams=3)
```

ในฐานะที่เป็นผลข้างเคียงนี่อาจช่วยให้คุณปวดหัวในแง่ของความสามารถในการทำซ้ำ :)


1

จากคำตอบของ @ Dirk และ @ Tony ฉันได้ทำการอัพเดทเล็กน้อย ผลที่ได้คือเอาท์พุท[1]ก่อนค่าขนาดสวยดังนั้นฉันเอาออกcapture.outputซึ่งแก้ปัญหา:

.ls.objects <- function (pos = 1, pattern, order.by,
                     decreasing=FALSE, head=FALSE, n=5) {
napply <- function(names, fn) sapply(names, function(x)
    fn(get(x, pos = pos)))
names <- ls(pos = pos, pattern = pattern)
obj.class <- napply(names, function(x) as.character(class(x))[1])
obj.mode <- napply(names, mode)
obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
obj.prettysize <- napply(names, function(x) {
    format(utils::object.size(x),  units = "auto") })
obj.size <- napply(names, utils::object.size)

obj.dim <- t(napply(names, function(x)
    as.numeric(dim(x))[1:2]))
vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
obj.dim[vec, 1] <- napply(names, length)[vec]
out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
if (!missing(order.by))
    out <- out[order(out[[order.by]], decreasing=decreasing), ]
if (head)
    out <- head(out, n)

return(out)
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()

-1

ฉันพยายามรักษาจำนวนวัตถุให้เล็กเมื่อทำงานในโครงการขนาดใหญ่ที่มีขั้นตอนกลางมาก ดังนั้นแทนที่จะสร้างวัตถุที่ไม่ซ้ำกันจำนวนมากที่เรียกว่า

dataframe-> step1-> step2-> step3->result

raster-> multipliedRast-> meanRastF-> sqrtRast->resultRast

tempผมทำงานกับวัตถุชั่วคราวที่ผมเรียกว่า

dataframe-> temp-> temp-> temp->result

ซึ่งทำให้ฉันมีไฟล์ระดับกลางน้อยกว่าและภาพรวมเพิ่มเติม

raster  <- raster('file.tif')
temp <- raster * 10
temp <- mean(temp)
resultRast <- sqrt(temp)

เพื่อประหยัดหน่วยความจำเพิ่มเติมฉันสามารถลบtempเมื่อไม่ต้องการอีกต่อไป

rm(temp)

ถ้าฉันต้องไฟล์กลางหลายผมใช้temp1, temp2,temp3 ,

สำหรับการทดสอบผมใช้test, test2...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.