Spark:寻找缺失数字的程序

时间:2016-12-16 16:36:16

标签: apache-spark pyspark

我们假设我们有一个这样的数字列表:

lst = [1,2,4,5,9,10]  

我如何编写Spark程序以找出该列表中缺少的数字。该计划应返回:3,6,7,8。

我试过累加器,dint工作了。

2 个答案:

答案 0 :(得分:1)

您可以尝试使用sc.range创建一个完整范围的RDD,然后使用subtract函数:

lst = sc.parallelize([1,2,4,5,9,10])
max_value = lst.max()
full_data = sc.range(1, max_value)
missing_values = full_data.subtract(lst)

如果您事先知道完整列表的大小,则可以避免调用max()

答案 1 :(得分:0)

如果您不太担心拥有最佳解决方案,那么一种方法是首先广播您拥有的数据,然后并行化包含所有元素的集合,并根据广播数据对其进行过滤。

这样的东西
lst = [1,2,4,5,9,10]  
broadcastVar = sc.broadcast(lst)

all_elems = sc.parallelize([i+1 for i in range(10)])
all_elems.filter(lambda x: x not in broadcastVar.value)

如果你正在寻找适用于少量数据的东西,那么这很好。如果您有大量数据,那么这种方法很糟糕,不应该使用。

如果需要更好的解决方案,那么我会做以下

  1. 基本上对数据进行分区,使用RDD可以做一个输出的地图(parition,number)。您可以编写一个小函数来获取每个数字的分区号。因此,例如,如果您在此地图后面有2个执行程序,则会有类似[(1-5,1),(1-5,2),(1-5,4),(1-5,5),( 6-10,9),(6-10,10)]
  2. 按键分组,现在我们有[(1-5,[1,2,4,5]),(6-10,[9,10])]
  3. 迭代key指定范围的地图,与值中的元素进行比较,并返回不存在的元素列表。
  4. 然后,您可以编写结果或收集或使用它们进行任何操作。需要注意的一点是,例如,如果我使用了5个执行器,那么键将是1-2,3-4,5-6,7-8,9-10,键7-8不会有任何元素。为了避免这种情况,一个选项是将按键分组的rdd与[(1-2,-1),(3-4,-1),(5-6,-1),(7-8, -1),(9-10,-1)]。如果你有大量的数据,那么与整个工作相比,由此增加的开销非常小。

    此示例代码存在许多问题,但将其视为概念验证。

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.spark.api.java.JavaPairRDD;
    import org.apache.spark.api.java.JavaRDD;
    import org.apache.spark.api.java.JavaSparkContext;
    import org.apache.spark.sql.SparkSession;
    import org.spark_project.guava.collect.Lists;
    
    import scala.Tuple2;
    
    public class Main {
    
    public static void main(String[] args) {
    
        SparkSession spark = SparkSession.builder().appName("spark-missing-nr").master("local[*]").getOrCreate();
        JavaSparkContext sc = new JavaSparkContext(spark.sparkContext());
        Integer[] lst = new Integer[] { 1, 2, 4, 5, 9, 10 };
        JavaRDD<Integer> lstRDD = sc.parallelize(Arrays.asList(lst));
    
        // Partition the data by whether number is smaller/equal or larger than
        // 5
        JavaPairRDD<String, Integer> groupableRDD = lstRDD.mapToPair(i -> {
            String group = i <= 5 ? "1-5" : "6-10";
            return new Tuple2<String, Integer>(group, i);
        });
        // Group by key
        JavaPairRDD<String, Iterable<Integer>> groupedRDD = groupableRDD.groupByKey();
    
        // so now we have [(1-5,[1, 2, 4, 5]), (6-10,[9, 10])]
        System.out.println(groupedRDD.collect());
    
        // map where you iterate over range specified by key
        JavaRDD<List<Integer>> missingValuesLists = groupedRDD.map(t -> {
            Integer from = new Integer(t._1().split("-")[0]);
            Integer to = new Integer(t._1().split("-")[1]);
    
            List<Integer> valuesList = Lists.newArrayList(t._2());
            List<Integer> missingValues = new ArrayList<Integer>();
    
            // iterate over range specified by key
            for (int i = from; i < to + 1; i++) {
                if (!valuesList.contains(i)) {
                    missingValues.add(i);
                }
            }
            return missingValues;
        });
        // outputs [[3], [6, 7, 8]]
        System.out.println(missingValuesLists.collect());
        sc.close();
    }
    }