我有一个由(sensor_id, timestamp, data)
组成的数据集(sensor_id
是物联网设备的ID,时间戳是UNIX时间,数据是当时输出的MD5哈希值)。表中没有主键,但每行都是唯一的。
我需要找到所有sensor_id
个s1
和s2
对,这样这两个传感器至少有n
(n=50
个)条目{{ 1}}在它们之间是共同的,即在(timestamp, data)
不同的场合,它们在相同的时间戳发出相同的数据。
对于数据的大小感,我有10B行和~50M不同n
我相信大约有~5M对传感器-id在同一时间戳发出相同数据至少50次
Spark中最好的方法是什么?我尝试了各种方法(分组sensor_ids
和/或自我加入),但它们的复杂性非常昂贵。
答案 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()
时间的复杂性有点波动,因为我正在努力解决这个问题,但至少应该看到瓶颈问题。