จะพิมพ์เนื้อหาของ RDD ได้อย่างไร?


124

ฉันกำลังพยายามพิมพ์เนื้อหาของคอลเลกชั่นไปยังคอนโซล Spark

ฉันมีประเภท:

linesWithSessionId: org.apache.spark.rdd.RDD[String] = FilteredRDD[3]

และฉันใช้คำสั่ง:

scala> linesWithSessionId.map(line => println(line))

แต่สิ่งนี้ถูกพิมพ์:

res1: org.apache.spark.rdd.RDD [หน่วย] = MappedRDD [4] ที่แผนที่เวลา: 19

ฉันจะเขียน RDD ลงในคอนโซลหรือบันทึกลงในดิสก์เพื่อดูเนื้อหาได้อย่างไร


1
Hi! คุณได้อ่านความคิดเห็นเกี่ยวกับคำตอบที่คุณยอมรับหรือไม่? ดูเหมือนจะทำให้เข้าใจผิด
dk14

2
@ dk14 เห็นด้วยฉันได้กำหนดคำตอบที่ยอมรับใหม่แล้ว
blue-sky

RDD กำลังถูกผลักไสให้เป็นพลเมืองชั้นสองคุณควรใช้ DataFrame และshowวิธีการนี้
Thomas Decaux

คำตอบ:


235

หากคุณต้องการดูเนื้อหาของ RDD วิธีหนึ่งคือใช้collect():

myRDD.collect().foreach(println)

นั่นไม่ใช่ความคิดที่ดีเมื่อ RDD มีสายการบินนับพันล้าน ใช้take()เพื่อพิมพ์เพียงไม่กี่ชิ้น:

myRDD.take(n).foreach(println)

1
ถ้าฉันใช้ foreach บน RDD (ซึ่งมีหลายล้านบรรทัด) เพื่อเขียนเนื้อหาลงใน HDFS เป็นไฟล์เดียวจะทำงานได้โดยไม่มีปัญหาใด ๆ ในคลัสเตอร์หรือไม่
Shankar

เหตุผลที่ฉันไม่ได้ใช้saveAsTextFileกับ RDD คือฉันต้องเขียนเนื้อหา RDD ลงในไฟล์มากกว่าหนึ่งไฟล์นั่นคือเหตุผลที่ฉันใช้foreach
Shankar

หากคุณต้องการบันทึกเป็นไฟล์เดียวคุณสามารถรวม RDD ของคุณเป็นพาร์ติชันเดียวก่อนที่จะเรียกใช้ saveAsTextFile แต่อาจทำให้เกิดปัญหาอีกครั้ง ฉันคิดว่าตัวเลือกที่ดีที่สุดคือการเขียนหลายไฟล์ใน HDFS จากนั้นใช้ hdfs dfs --getmerge เพื่อรวมไฟล์
Oussama

คุณบอกว่าเมื่อใช้ foreach บน RDD มันจะยังคงอยู่ใน RAM ของไดรเวอร์คำชี้แจงถูกต้องหรือไม่? เพราะสิ่งที่ฉันเข้าใจคือ foreach จะทำงานกับคนงานแต่ละคน [คลัสเตอร์] ไม่ใช่บนไดรเวอร์
Shankar

saveAsTextFile จะเขียนหนึ่งไฟล์ต่อพาร์ติชันซึ่งเป็นสิ่งที่คุณต้องการ (หลายไฟล์) หรือตามที่ Oussama แนะนำคุณสามารถทำ rdd.coalesce (1) .saveAsTextFile () เพื่อให้ได้ไฟล์เดียว หาก RDD มีพาร์ติชันน้อยเกินไปสำหรับความต้องการของคุณคุณสามารถลอง rdd.repartition (N) .saveAsTextFile ()
foghorn

49

mapฟังก์ชั่นคือการเปลี่ยนแปลงซึ่งหมายความว่า Spark จะไม่ประเมิน RDD ของคุณจนกว่าคุณเรียกใช้การดำเนินการเกี่ยวกับมัน

ในการพิมพ์คุณสามารถใช้foreach(ซึ่งเป็นการกระทำ):

linesWithSessionId.foreach(println)

ในการเขียนลงดิสก์คุณสามารถใช้หนึ่งในsaveAs...ฟังก์ชัน (การดำเนินการที่ยังคงอยู่) จากRDD API


6
บางทีคุณอาจต้องพูดถึงcollectเพื่อให้พิมพ์ RDD ในคอนโซลได้
zsxwing

1
foreachตัวมันเองจะ "เป็นจริง" RDD ก่อนแล้วจึงเรียกใช้printlnในแต่ละองค์ประกอบดังนั้นจึงcollectไม่จำเป็นจริงๆที่นี่ (แม้ว่าคุณจะสามารถใช้งานได้ก็ตาม) ...
fedragon

5
จริงๆแล้วถ้าไม่มี collect () ก่อน foreach ฉันไม่เห็นอะไรเลยบนคอนโซล
Vittorio Cozzolino

3
จริงๆแล้วมันใช้งานได้ดีใน Spark shell ของฉันแม้ใน 1.2.0 แต่ฉันคิดว่าฉันรู้ว่าความสับสนนี้มาจากไหน: คำถามเดิมถามว่าจะพิมพ์ RDD ไปยังคอนโซล Spark ได้อย่างไร (= เชลล์) ดังนั้นฉันจึงคิดว่าเขาจะทำงานในพื้นที่ซึ่งในกรณีนี้ก็foreachใช้ได้ดี หากคุณกำลังเรียกใช้งานบนคลัสเตอร์และคุณต้องการพิมพ์ rdd ของคุณคุณควรcollect(ตามที่ระบุไว้ในความคิดเห็นและคำตอบอื่น ๆ ) เพื่อที่จะส่งไปยังไดรเวอร์ก่อนที่printlnจะดำเนินการ และการใช้takeตามที่ Oussama แนะนำอาจเป็นความคิดที่ดีหาก RDD ของคุณใหญ่เกินไป
fedragon

6
คำตอบข้างบนแย่ คุณควรยกเลิกการยอมรับ Foreach จะไม่พิมพ์ไปยังคอนโซลมันจะพิมพ์บนโหนดของผู้ปฏิบัติงานของคุณ หากคุณมีเพียงโหนดเดียว foreach จะทำงาน แต่ถ้าคุณมีโหนดเดียวทำไมคุณถึงใช้ประกายไฟ? เพียงแค่ใช้ SQL awk หรือ Grep หรือสิ่งที่ง่ายกว่านั้น ดังนั้นฉันคิดว่าคำตอบเดียวที่ถูกต้องคือการรวบรวม หากการรวบรวมมีขนาดใหญ่สำหรับคุณและคุณต้องการเพียงตัวอย่างการใช้งานหรือฟังก์ชั่น head หรือ simillar ตามที่อธิบายด้านล่าง
eshalev

12

หากคุณกำลังเรียกใช้สิ่งนี้บนคลัสเตอร์printlnจะไม่พิมพ์กลับไปที่บริบทของคุณ คุณต้องนำRDDข้อมูลไปยังเซสชันของคุณ ในการดำเนินการนี้คุณสามารถบังคับให้เป็นอาร์เรย์ภายในแล้วพิมพ์ออกมา:

linesWithSessionId.toArray().foreach(line => println(line))

12

คุณสามารถแปลงของคุณRDDไปDataFrameแล้วshow()มัน

// For implicit conversion from RDD to DataFrame
import spark.implicits._

fruits = sc.parallelize([("apple", 1), ("banana", 2), ("orange", 17)])

// convert to DF then show it
fruits.toDF().show()

ซึ่งจะแสดงข้อมูล 20 บรรทัดแรกดังนั้นขนาดของข้อมูลของคุณจึงไม่น่าจะเป็นปัญหา

+------+---+                                                                    
|    _1| _2|
+------+---+
| apple|  1|
|banana|  2|
|orange| 17|
+------+---+

1
ฉันคิดว่ามันคือimport spark.implicits._
Ryan Hartman

ที่นี่ใช้ห้องสมุดอะไร ฉันไม่สามารถตรวจสอบค่าtoDFมิได้spark.implicits._อยู่ในขอบเขตจุดประกาย
Sergii

1

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




1

แทนที่จะพิมพ์ทุกครั้งคุณสามารถ;

[1] สร้างวิธีการพิมพ์ทั่วไปภายใน Spark Shell

def p(rdd: org.apache.spark.rdd.RDD[_]) = rdd.foreach(println)

[2] หรือดีกว่าโดยใช้นัยคุณสามารถเพิ่มฟังก์ชันลงในคลาส RDD เพื่อพิมพ์เนื้อหาได้

implicit class Printer(rdd: org.apache.spark.rdd.RDD[_]) {
    def print = rdd.foreach(println)
}

ตัวอย่างการใช้งาน:

val rdd = sc.parallelize(List(1,2,3,4)).map(_*2)

p(rdd) // 1
rdd.print // 2

เอาท์พุท:

2
6
4
8

สำคัญ

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



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