Spark:查找至少具有n个共同属性的对?

时间:2017-03-11 22:58:29

标签: algorithm apache-spark apache-spark-sql spark-streaming spark-dataframe

我有一个由(sensor_id, timestamp, data)组成的数据集(sensor_id是物联网设备的ID,时间戳是UNIX时间,数据是当时输出的MD5哈希值)。表中没有主键,但每行都是唯一的。

我需要找到所有sensor_ids1s2对,这样这两个传感器至少有nn=50个)条目{{ 1}}在它们之间是共同的,即在(timestamp, data)不同的场合,它们在相同的时间戳发出相同的数据。

对于数据的大小感,我有10B行和~50M不同n我相信大约有~5M对传感器-id在同一时间戳发出相同数据至少50次

Spark中最好的方法是什么?我尝试了各种方法(分组sensor_ids和/或自我加入),但它们的复杂性非常昂贵。

3 个答案:

答案 0 :(得分:4)

这是一个伪代码,从Spark中抽象出来。您可以先对数据集进行排序:

select id, timestamp, data order by timestamp, data, id

示例性10行:

s1,100,a  #1
s2,100,a  #2
s3,100,a  #3
s4,100,b  #4
s1,101,a  #5
s3,101,b  #6
s4,101,b  #7
s2,101,a  #8
s3,102,b  #9
s4,102,b  #10

现在从上到下迭代,只要时间戳和数据与上一个条目相同,就构建一个条目列表。

在我们的示例中,行1-3形成了这样的列表,因此我们已经看到了一些潜在的对:

s1, s2
s1, s3
s2, s3

第4行只是一个带有(100,b)的条目,我们可以跳过它。 第5行只有一个条目(101,a),我们可以跳过它。

第6行和第7行是新配对:

s3, s4

#9和#10形成一对

将所有这些放在一起可以很容易地计算对:

s1, s2
s1, s3
s2, s3
s3, s4
s3, s4

此方法的好处是,如果您可以对文件进行排序,则可以将已排序的数据集拆分为多个较小的块(块应该在组边界上拆分 - 即#1,2,3应该在一个块中),计算对,并作为最后一步加入最终结果。

我希望这会有所帮助。

答案 1 :(得分:0)

如果我的理解是正确的,那么我可以通过使用下面的简单代码来实现这一点,

test("Spark: Find pairs having atleast n common attributes"){
/**
  * s1,1210283218710,34
    s1,1210283218730,24
    s1,1210283218750,84
    s1,1210283218780,54
    s2,1210283218710,34
    s2,1210283218730,24
    s2,1210283218750,84
    s2,1210283218780,54
    s3,1210283218730,24
    s3,1210283218750,84
    s3,1210283218780,54
  */
  val duplicateSensors = sc.textFile("sensor_data")
  .map(line => line.split(",")).map(ar=>((ar(1),ar(2)),ar(0) )) // (ts,val),sid
  .aggregateByKey(List.empty[String])(_ :+_,_:::_)// grouped(ts,val)(List(n sid))
  .flatMapValues(l => l.sorted.combinations(2))// (ts,val)(List(2 sid combination))
  .map(_._2).countByValue() // List(s1, s3) -> 3, List(s2, s3) -> 3, List(s1, s2) -> 4 (2sensors, no of common entries)
   // Now Do the filter .... grater than 50
  duplicateSensors.foreach(println)
}

你将得到具有共同属性的对。

答案 2 :(得分:0)

我是这样做的。

首先,生成一些假数据:

#!/usr/bin/env python3
import random

fout = open('test_data.csv','w')

i=0
for x in range(100000):
  if i>=1000000:
    break
  for y in range(random.randint(0,100)):
    i         = i + 1
    timestamp = x
    sensor_id = random.randint(0,50)
    data      = random.randint(0,1000)
    fout.write("{} {} {}\n".format(timestamp,sensor_id,data))

现在,您可以按如下方式处理数据。

如果您让行数 N ,则唯一时间戳的数量为 T ,并且每个时间戳的预期传感器数量为 S ,然后每个操作的复杂性如注释

import itertools

#Turn a set into a list of all unique unordered pairs in the set, without
#including self-pairs
def Pairs(x):
  temp = []
  x    = list(x)
  for i in range(len(x)):
    for j in range(i+1,len(x)):
      temp.append((x[i],x[j]))
  return temp

#Load data
#O(N) time to load data
fin        = sc.textFile("file:///z/test_data.csv")
#Split data at spaces, keep only the timestamp and sensorid portions
#O(N) time to split each line of data
lines      = fin.map(lambda line: line.split(" ")[0:2])
#Convert each line into a timestamp-set pair, where the set contains the sensor
#O(N) time to make each line into a timestamp-hashset pair
monosets   = lines.map(lambda line: (line[0],set(line[1])))
#Combine sets by timestamp to produce a list of timestamps and all sensors at
#each timestamp
#O(TS) time to place each line into a hash table of size O(T) where each 
#entry in the hashtable is a hashset combining 
timegroups = sets.reduceByKey(lambda a,b: a | b)
#Convert sets at each timestamp into a list of all pairs of sensors that took
#data at that timestamp
#O(T S^2) time to do all pairs for each timestamp
shared     = timegroups.flatMap(lambda tg: PairsWithoutSelf(tg[1]))
#Associate each sensor pair with a value one
#O(T S^2) time
monoshared = shared.map(lambda x: (x,1))
#Sum by sensor pair
#O(T S^2) time
paircounts = monoshared.reduceByKey(lambda a,b: a+b)
#Filter by high hitters
#O(<S^2) time
good       = paircounts.filter(lambda x: x[1]>5)
#Display results
good.count()

时间的复杂性有点波动,因为我正在努力解决这个问题,但至少应该看到瓶颈问题。