【问题标题】:Spark Dataset Foreach function does not iterateSpark Dataset Foreach 函数不迭代
【发布时间】:2021-12-23 03:29:33
【问题描述】:

上下文

我想遍历 Spark Dataset 并为每一行更新一个 HashMap。

这是我的代码:

// At this point, I have a my_dataset variable containing 300 000 rows and 10 columns
// - my_dataset.count() == 300 000
// - my_dataset.columns().length == 10

// Declare my HashMap
HashMap<String, Vector<String>> my_map = new HashMap<String, Vector<String>>();

// Initialize the map
for(String col : my_dataset.columns())
{
    my_map.put(col, new Vector<String>());
}

// Iterate over the dataset and update the map
my_dataset.foreach( (ForeachFunction<Row>) row -> {
    for(String col : my_map.KeySet())
    {
        my_map.get(col).add(row.get(row.fieldIndex(col)).toString());
    }
});

问题

我的问题是 foreach 根本不迭代,lambda 永远不会执行,我不知道为什么。
我按照此处的说明实现了它:How to traverse/iterate a Dataset in Spark Java?

最后,尽管 Dataset 不是,所有内部向量都保持为空(因为它们被初始化)(查看给定代码示例中的第一个 cmets)。

我知道 foreach 永远不会迭代,因为我做了两个测试:

  • 添加一个AtomicInteger 来计算迭代次数,使用incrementAndGet() 方法在lambda 的开头将其递增。 => 计数器值在进程结束时保持0
  • 在 lambda 的开头打印一条调试消息。 => 消息永远不会显示。

我没有使用过 Java(更不用说使用 Java lambdas),所以我可能错过了一个重要的点,但我找不到什么。

【问题讨论】:

    标签: java apache-spark lambda foreach apache-spark-dataset


    【解决方案1】:

    我可能有点老派,但我从不太喜欢 lambda,因为它会变得非常复杂。

    这是foreach()的完整示例:

    package net.jgp.labs.spark.l240_foreach.l000;
    
    import java.io.Serializable;
    
    import org.apache.spark.api.java.function.ForeachFunction;
    import org.apache.spark.sql.Dataset;
    import org.apache.spark.sql.Row;
    import org.apache.spark.sql.SparkSession;
    
    public class ForEachBookApp implements Serializable {
      private static final long serialVersionUID = -4250231621481140775L;
    
      private final class BookPrinter implements ForeachFunction<Row> {
        private static final long serialVersionUID = -3680381094052442862L;
    
        @Override
        public void call(Row r) throws Exception {
          System.out.println(r.getString(2) + " can be bought at " + r.getString(
              4));
        }
      }
    
      public static void main(String[] args) {
        ForEachBookApp app = new ForEachBookApp();
        app.start();
      }
    
      private void start() {
        SparkSession spark = SparkSession.builder().appName("For Each Book").master(
            "local").getOrCreate();
    
        String filename = "data/books.csv";
        Dataset<Row> df = spark.read().format("csv").option("inferSchema", "true")
            .option("header", "true")
            .load(filename);
        df.show();
    
        df.foreach(new BookPrinter());
      }
    }
    

    如您所见,此示例读取一个 CSV 文件并从数据中打印一条消息。这很简单。

    foreach() 实例化一个新类,工作已在其中完成。

    df.foreach(new BookPrinter());
    

    工作在类的call()方法中完成:

      private final class BookPrinter implements ForeachFunction<Row> {
    
        @Override
        public void call(Row r) throws Exception {
    ...
        }
      }
    

    由于您是 Java 新手,请确保您拥有正确的签名(用于类和方法)和正确的导入。

    您也可以从https://github.com/jgperrin/net.jgp.labs.spark/tree/master/src/main/java/net/jgp/labs/spark/l240_foreach/l000 克隆示例。这应该可以帮助您处理foreach()

    【讨论】:

    • 我明白了,但我不想打印行,我想更新一个在 ForeachFunction&lt;Row&gt; 的派生对象中不知道的 HashMap。我会尝试将我的变量设为全局变量(或者更好的是,成为外部类的成员,以便知道它),我会告诉你它是否解决了我的问题。
    • 我更建议这段代码来看看你是否可以遍历你的数据框,看看是否会发生任何事情并从那里构建,然后专注于地图......你对 Spark 的架构有多熟悉事情在哪里运行?主要代码在驱动节点上运行,ForeachFunction 会被序列化到执行节点,所以你将无法在两者之间共享内存对象...
    • 我得到一个 NotSerializableException。我真的不了解 Spark,我只是需要使用它。但我想你最后的评论可能会解释为什么如果无法执行我的代码它永远不会迭代。但是如何?只在 Spark 中迭代一个容器是不可能的?就像一个基本的 for 循环。
    • 也许你应该开始另一个问题,我也很乐意回答......但简而言之,使用 Spark,你将拥有这个包含数据的逻辑容器并提供一个非常强大的API,数据框。处理数据的方式是你“碾压”数据框,直到你得到你想要的数据的形状,然后你把它转储到你需要的地方。当您使用 Spark API 转换数据时,您会利用分布式处理......这有意义吗?
    • 哦,我明白了,最后因为我只有 300 000 x 300 000 行,所以我使用函数 collectAsList() 进行迭代,即使我不喜欢在内存中复制类似的所有内容只是为了迭代它......感谢您的帮助!你的回答对我没有帮助,但你的 cmets 做了,所以我给你打勾:)
    猜你喜欢
    • 2017-06-30
    • 2021-12-18
    • 2017-09-18
    • 1970-01-01
    • 2017-08-28
    • 2019-03-07
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多