สำหรับ Loop ใน R พร้อมตัวอย่างสำหรับรายการและเมทริกซ์

⚡ สรุปอย่างชาญฉลาด

ลูป for ใน R จะรันบล็อกโค้ดหนึ่งครั้งสำหรับแต่ละองค์ประกอบของลำดับ ไม่ว่าลำดับนั้นจะเป็นเวกเตอร์ รายการ หรือเมทริกซ์ บทความนี้จะวนซ้ำไปทีละกรณีและแสดงให้เห็นว่าเมื่อใดที่การเขียนโค้ดแบบเวกเตอร์เป็นตัวเลือกที่ดีกว่า

  • 🔁 ไวยากรณ์หลัก: for (i in vector) { expression } จะกำหนดค่าแต่ละองค์ประกอบให้กับ i ทีละตัว และรันส่วนเนื้อหาของนิพจน์
  • 📋 วัตถุใดๆ: ไวยากรณ์เดียวกันนี้สามารถใช้กับเวกเตอร์ รายการ ชุดคอลัมน์ของเฟรมข้อมูล หรือแถวของเมทริกซ์ได้
  • 🧮 ลูปซ้อนกัน: เมทริกซ์ต้องการลูปสองลูป ลูปภายนอกสำหรับแถว และลูปภายในสำหรับคอลัมน์
  • 🛑 การควบคุมการไหล: คำสั่ง break จะออกจากลูปโดยสมบูรณ์ และคำสั่ง next จะข้ามเฉพาะองค์ประกอบปัจจุบันเท่านั้น
  • การแปลงเป็นเวกเตอร์ก่อน: การดำเนินการแบบเวกเตอร์ในตัวและตระกูลคำสั่ง apply นั้นเร็วกว่าและใช้เวลาน้อยกว่าการวนลูปแบบระบุชัดเจน
  • ⚠️ ลำดับที่ปลอดภัย: ให้ใช้ seq_along(x) แทน 1:length(x) ซึ่งจะทำงานผิดพลาดเมื่ออ็อบเจ็กต์ว่างเปล่า

ลูป for ในเมทริกซ์รายการ R

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

ไวยากรณ์ของลูป for ในภาษา R

for (i in vector) {
    Exp
}

ที่นี่

R จะวนซ้ำตัวแปรทั้งหมดในเวกเตอร์และทำการคำนวณที่เขียนภายใน exp

สำหรับ Loop ใน R
สำหรับ Loop ใน R

เรามาดูตัวอย่างกัน

สำหรับการวนซ้ำใน R ตัวอย่างที่ 1: เราวนซ้ำองค์ประกอบทั้งหมดของเวกเตอร์และพิมพ์ค่าปัจจุบัน

# Create fruit vector
fruit <- c('Apple', 'Orange', 'Passion fruit', 'Banana')
# Create the for statement
for ( i in fruit){ 
 print(i)
}

Output:

## [1] "Apple"
## [1] "Orange"
## [1] "Passion fruit"
## [1] "Banana"

สำหรับการวนซ้ำใน R ตัวอย่างที่ 2ยกกำลังสองจำนวนเต็มทุกจำนวนตั้งแต่ 1 ถึง 4 แล้วเก็บผลลัพธ์ไว้ในลิสต์

# Create an empty list
list <- c()
# Create a for statement to populate the list
for (i in seq(1, 4, by=1)) {
  list[[i]] <- i*i
}
print(list)

Output:

## [1]   1  4 9 16

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

วิธีการใช้ break และ next ในลูป For

คำหลักสองคำขัดจังหวะลำดับการทำงานปกติของลูป for

  • ทำลาย ออกจากลูปทันทีและข้ามองค์ประกอบที่เหลือทั้งหมด
  • ถัดไป ละทิ้งองค์ประกอบปัจจุบันและไปยังองค์ประกอบถัดไป
fruit <- c('Apple', 'Orange', 'Passion fruit', 'Banana')

# break: stop at the first match
for (i in fruit) {
    if (i == 'Passion fruit') {
        print('Found it, stopping here')
        break
    }
    print(i)
}

# next: skip one element and carry on
for (i in fruit) {
    if (i == 'Orange') next
    print(i)
}

ในลูปซ้อนกัน ให้ใช้คำสั่ง break และ next เฉพาะกับลูปชั้นในสุดที่บรรจุคำสั่งเหล่านั้นเท่านั้น ในตัวอย่างเมทริกซ์ หากต้องการออกจากทั้งสองลูป ให้ตั้งค่าตัวแปรแฟล็กและตรวจสอบค่าในลูปชั้นนอกด้วย

ใช้ seq_along() สำหรับดัชนี การเขียน for (i in 1:length(x)) ดูเหมือนจะไม่เป็นอันตราย แต่จะล้มเหลวเมื่อใช้กับอ็อบเจ็กต์ว่าง เนื่องจาก 1:0 จะสร้างลำดับ 1, 0 และลูปจะทำงานสองครั้ง ส่วน seq_along(x) จะส่งคืนลำดับว่างแทน ดังนั้นจึงข้ามลูปไปได้อย่างถูกต้อง:

for (i in seq_along(fruit)) {
    cat(i, fruit[i], '\n')
}

วนลูป for ผ่านรายการ

ห้องน้ำping การใช้รายการแบบเดิม ๆ ทำงานเหมือนกับการใช้ห้องน้ำทุกประการping โดยใช้เวกเตอร์เป็นตัวนำ ยกเว้นว่าแต่ละองค์ประกอบสามารถเก็บค่าประเภทที่แตกต่างกันได้:

# Create a list with three vectors
fruit <- list(Basket = c('Apple', 'Orange', 'Passion fruit', 'Banana'), 
Money = c(10, 12, 15), purchase = FALSE)
for (p  in fruit) 
{ 
	print(p)
}

Output:

## [1] "Apple" "Orange" "Passion fruit" "Banana"       
## [1] 10 12 15
## [1] FALSE

วนลูป for บนเมทริกซ์

เมทริกซ์มีสองมิติ คือ แถวและคอลัมน์ ดังนั้นการวนซ้ำในแต่ละเซลล์จึงต้องใช้ลูป for ซ้อนกันสองชั้น: ลูปภายนอกสำหรับแถว และลูปภายในสำหรับคอลัมน์

# Create a matrix
mat <- matrix(data = seq(10, 20, by=1), nrow = 6, ncol =2)
# Create the loop with r and c to iterate over the matrix
for (r in 1:nrow(mat))   
    for (c in 1:ncol(mat))  
         print(paste("Row", r, "and column",c, "have values of", mat[r,c]))  

Output:

## [1] "Row 1 and column 1 have values of 10"
## [1] "Row 1 and column 2 have values of 16"
## [1] "Row 2 and column 1 have values of 11"
## [1] "Row 2 and column 2 have values of 17"
## [1] "Row 3 and column 1 have values of 12"
## [1] "Row 3 and column 2 have values of 18"
## [1] "Row 4 and column 1 have values of 13"
## [1] "Row 4 and column 2 have values of 19"
## [1] "Row 5 and column 1 have values of 14"
## [1] "Row 5 and column 2 have values of 20"
## [1] "Row 6 and column 1 have values of 15"
## [1] "Row 6 and column 2 have values of 10" 

วนลูป for บน DataFrame ในภาษา R

ดาต้าเฟรมคือรายการของคอลัมน์ ดังนั้นลูป for ธรรมดาจะวนไปตามคอลัมน์แทนที่จะเป็นแถว ซึ่งโดยปกติแล้วจะเป็นสิ่งที่คุณต้องการ:

df <- data.frame(a = 1:5, b = 6:10, c = 11:15)

# Loop over the columns
for (col in names(df)) {
    cat(col, 'has mean', mean(df[[col]]), '\n')
}

โปรดสังเกตวงเล็บสองชั้น df[[col]] extracts ส่งคืนคอลัมน์เป็นเวกเตอร์ ในขณะที่ df[col] ส่งคืนเฟรมข้อมูลแบบคอลัมน์เดียว ซึ่งฟังก์ชัน mean() ไม่สามารถจัดการได้

ห้องน้ำping ข้ามแถว การประมวลผลแบบวนซ้ำตามแถวนั้นเป็นไปได้ แต่ช้า เนื่องจาก R จะคัดลอกแถวนั้นในทุกรอบการประมวลผล:

for (r in seq_len(nrow(df))) {
    cat('Row', r, 'sums to', sum(df[r, ]), '\n')
}

สำหรับข้อมูลที่มีมากกว่าสองสามพันแถว ควรใช้ rowSums(df), apply(df, 1, sum) หรือ dplyr แทน group_by() และ summarise() ไปป์ไลน์ ซึ่งทั้งหมดนี้ทำงานเหมือนกันโดยไม่ต้องคัดลอกข้อมูล

ลูป For เทียบกับการใช้เวกเตอร์ใน R

R เป็นภาษาที่ใช้การประมวลผลแบบเวกเตอร์: ตัวดำเนินการและฟังก์ชันส่วนใหญ่ของมันทำงานกับเวกเตอร์ทั้งหมดพร้อมกันอยู่แล้วในโค้ด C ที่คอมไพล์แล้ว การวนลูปแบบชัดเจนที่ทำซ้ำการดำเนินการแบบเวกเตอร์ทีละองค์ประกอบนั้นทั้งยาวกว่าและช้ากว่า

# Loop version
squares <- c()
for (i in 1:4) {
    squares[i] <- i * i
}

# Vectorized version, same result
squares <- (1:4)^2
งาน ห่วง เทียบเท่าเวกเตอร์
การคำนวณเลขคณิตแบบทีละองค์ประกอบ สำหรับองค์ประกอบแต่ละอย่าง x * 2, x + y, x^2
ใช้ฟังก์ชันกับแต่ละคอลัมน์ สำหรับชื่อที่มากกว่า (df) sapply(df, mean)
ใช้ฟังก์ชันกับแต่ละแถว สำหรับความยาวลำดับเกิน (nrow(df)) apply(df, 1, sum)
สร้างรายการผลลัพธ์ สำหรับรายการที่มี list[[i]] <- … ลัปพลี (กริยา, ฉ)
การเข้ารหัสแบบมีเงื่อนไข สำหรับด้วยเงื่อนไขและมิฉะนั้น ถ้ามิฉะนั้น (เงื่อนไข, a, b)

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

ถ้าจำเป็นต้องใช้ลูป ให้จัดสรรหน่วยความจำล่วงหน้า การขยายขนาดวัตถุภายในลูปจะบังคับให้ R คัดลอกวัตถุนั้นในทุกรอบการทำงาน ดังนั้นควรสร้างวัตถุให้มีขนาดเต็มก่อน:

# Slow: the vector is reallocated on every pass
out <- c()
for (i in 1:1000) out[i] <- i^2

# Fast: allocated once
out <- numeric(1000)
for (i in 1:1000) out[i] <- i^2

ลูป for ใน R: คู่มือฉบับย่อ

โครงสร้างทุกอย่างที่ใช้ในบทเรียนนี้แสดงไว้ด้านล่างนี้:

วัตถุประสงค์ Code
วนลูปตามเวกเตอร์
for (i in v) { print(i) }
วนลูปผ่านดัชนีอย่างปลอดภัย
for (i in seq_along(v)) { print(v[i]) }
วนลูปผ่านรายการ
for (p in my_list) { print(p) }
วนลูปผ่านคอลัมน์ของดาต้าเฟรม
for (col in names(df)) { print(mean(df[[col]])) }
วนลูปผ่านเมทริกซ์
for (r in 1:nrow(m)) for (c in 1:ncol(m)) print(m[r, c])
ออกจากลูปก่อนกำหนด
if (condition) break
ข้ามไปหนึ่งรอบ
if (condition) next
จัดสรรเอาต์พุตล่วงหน้า
out <- numeric(n)

คำถามที่พบบ่อย

เมื่อ x ว่างเปล่า 1:length(x) จะส่งคืนลำดับ 1, 0 และลูปจะทำงานสองครั้งกับองค์ประกอบที่ไม่มีอยู่จริง seq_along(x) จะส่งคืนลำดับว่างเปล่า ดังนั้นลูปจึงถูกข้ามไปอย่างถูกต้อง

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

คอลัมน์ ดาต้าเฟรมคือรายการของคอลัมน์ ดังนั้นในแต่ละรอบการวนซ้ำ คุณจะได้รับคอลัมน์ทั้งหมดหนึ่งคอลัมน์ หากต้องการวนซ้ำตามแถว ให้ใช้ seq_len(nrow(df)) อย่างชัดเจน

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

ใช่แล้ว ผู้ช่วย AI สามารถแปลงลูปแบบทีละองค์ประกอบส่วนใหญ่ให้เป็นแบบเวกเตอร์หรือแบบใช้ตระกูลฟังก์ชันได้ เปรียบเทียบผลลัพธ์ของทั้งสองเวอร์ชันด้วยฟังก์ชัน identical() ก่อนที่จะแทนที่ของเดิม

สรุปโพสต์นี้ด้วย: