-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtask2.py
More file actions
289 lines (224 loc) · 9.42 KB
/
Copy pathtask2.py
File metadata and controls
289 lines (224 loc) · 9.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
from pyspark import SparkContext
from itertools import combinations
import sys
import math
import time
from itertools import combinations
def print_results(results,type):
# Initialize the current size and the output string
current_size = 1
output = f"{type}\n"
# Iterate over the results
for itemset, count in results:
# If the itemset is a singleton, convert it to a tuple
if not isinstance(itemset, tuple):
itemset = (itemset,)
# If the size of the current itemset is different from the current size, add a newline to the output
if len(itemset) != current_size:
output = output[:-1] + "\n\n" # Remove the trailing comma from the previous line
current_size = len(itemset)
# Add the current itemset to the output
if len(itemset) == 1:
output += "('" + itemset[0] + "'),"
else:
output += str(itemset) + ","
# Remove the trailing comma from the output
output = output[:-1]
# Print the output
print(output)
def write_results(results, type, file):
# Initialize the current size and the output string
current_size = 1
output = f"{type}\n"
# Iterate over the results
for itemset, count in results:
# If the itemset is a singleton, convert it to a tuple
if not isinstance(itemset, tuple):
itemset = (itemset,)
# If the size of the current itemset is different from the current size, add a newline to the output
if len(itemset) != current_size:
output = output[:-1] + "\n\n" # Remove the trailing comma from the previous line
current_size = len(itemset)
# Add the current itemset to the output
if len(itemset) == 1:
output += "('" + itemset[0] + "'),"
else:
output += str(itemset) + ","
# Remove the trailing comma from the output
output = output[:-1]
# putting a spce between the two
if type == 'Frequent Itemsets:':
output = "\n\n" + output
# Write the output to the file
file.write(output)
def process_data(rdd):
# Split the data
rdd = rdd.map(lambda line: [x.replace('"', '') for x in line.split(",")])
# Remove the header
header = rdd.first()
rdd = rdd.filter(lambda line: line != header)
# Select TRANSACTION_DT, CUSTOMER_ID, PRODUCT_ID and rename header
rdd = rdd.map(lambda item: (item[0], int(item[1]), int(item[5]))).map(lambda header: (header[0] + "-" + str(header[1]), str(header[2])))
# Collect the data to the driver
rdd_data = rdd.collect()
return rdd_data
def preprocessing(file,filter):
# reading in the data to sere if its case 1 or case 2
text_rdd = sc.textFile(file)
text_rdd_header = text_rdd.first()
data_rdd = text_rdd.filter(lambda row: row != text_rdd_header)
rdd = data_rdd.map(lambda row: (row.split(',')[0], row.split(',')[1])).groupByKey().mapValues(set).map(lambda item: sorted(list(item[1])))
rdd = rdd.filter(lambda x: len(x) > filter)
return rdd
def counting_baskets(rdd):
count = rdd.count()
return count
def PCY(iterator, support, n_baskets):
# Convert iterator to list
partition = list(iterator)
# Calculate for each basket given p*s
p = len(partition) / n_baskets
s = int(support)
support_thres= math.ceil(p*s)
singletons = {}
num_buckets = len(partition)
bucketCounts = {}
# First pass
for basket in partition:
for item in basket:
if item not in singletons:
singletons[item] = 0
singletons[item] += 1
for pair in combinations(basket, 2):
bucket = hash(pair) % num_buckets
if bucket not in bucketCounts:
bucketCounts[bucket] = 0
bucketCounts[bucket] += 1
# Getting the frequent pairs and making sure they are actually frequent
single_frequent = set()
for item, count in singletons.items():
if count >= support_thres:
single_frequent.add(item)
# Create bitmap and getting the frequent bitmaps
bitmap = [0] * num_buckets
for bucket, count in bucketCounts.items():
if count >= support_thres:
bitmap[bucket] = 1
candidate_pairs = {}
# Second pass
for basket in partition:
for pair in combinations(basket, 2):
sorted_pair = tuple(sorted(pair))
# if the singletons are frequent
if set(sorted_pair).issubset(single_frequent):
bucket = hash(sorted_pair) % num_buckets
# if bitmap is 1
if bitmap[bucket] == 1:
if sorted_pair not in candidate_pairs:
candidate_pairs[sorted_pair] = 0
candidate_pairs[sorted_pair] += 1
# Getting the frequent singletons
pairs_frequent = set()
for item, count in candidate_pairs.items():
if count >= support_thres:
pairs_frequent.add(item)
# frequent singletons
for item in single_frequent:
yield (item, singletons[item])
# frequent pairs
for pair in pairs_frequent:
yield (pair, candidate_pairs[pair])
# Generate larger frequent itemsets
frequent_items= pairs_frequent
k = 3
while True:
candidate_itemsets = set()
unique_items = set(a for b in frequent_items for a in b)
# getting the canidates
for itemset in combinations(unique_items, k):
sorted_itemset = tuple(sorted(itemset))
if sorted_itemset not in candidate_itemsets and all(subset in frequent_items for subset in combinations(sorted_itemset, k-1)):
candidate_itemsets.add(sorted_itemset)
# pruning the canidates
frequent_items = set()
for itemset in candidate_itemsets:
sorted_itemset = tuple(sorted(itemset))
count = sum(1 for basket in partition if set(sorted_itemset).issubset(basket))
if count >= support_thres:
frequent_items.add(sorted_itemset)
yield (sorted_itemset, count)
if not frequent_items:
break
k += 1
def count_itemsets(iterator, candidates):
partition = list(iterator)
itemset_counts = {}
for item in candidates:
# If the itemset is a singleton, convert it to a tuple
if not isinstance(item, tuple):
item = (item,)
for basket in partition:
if set(item).issubset(set(basket)):
if item in itemset_counts:
itemset_counts[item] += 1
else:
itemset_counts[item] = 1
for item, count in itemset_counts.items():
yield (item, count)
def son_pass_1(rdd, support, n_baskets):
# First pass of SON algorithm -- use reducer to get only the distinct items
candidate_itemsets = rdd.mapPartitions(lambda iterator: PCY(iterator, support, n_baskets)).reduceByKey(lambda x, y: x + y)
can_results = candidate_itemsets.collect()
# Sort the results lexicographically
can_results.sort(key=lambda x: (len(x[0]) if isinstance(x[0], tuple) else 1, x[0] if isinstance(x[0], tuple) else (x[0],)))
candidates = [x[0] for x in can_results]
# Print the results
# print_results(can_results,'Candidates:')
return candidates, can_results
def son_pass_2(rdd, candidates, support):
son_passtwo = rdd.mapPartitions(lambda iterator: count_itemsets(iterator, candidates))
# Get the full itemsets
son_passtwo = son_passtwo.reduceByKey(lambda x, y: x + y)
# make sure they are actually frequent
frequent_itemsets = son_passtwo.filter(lambda x: x[1] >= support)
results = frequent_itemsets.collect()
# Sort the results lexicographically
results.sort(key=lambda x: (len(x[0]) if isinstance(x[0], tuple) else 1, x[0] if isinstance(x[0], tuple) else (x[0],)))
return results
if __name__ == '__main__':
# Load the data
time_start= time.time()
input_filepath = sys.argv[3]
intermediate_filepath = './result_inter.csv'
output_filepath = sys.argv[4]
sc= SparkContext('local[*]','task2')
rdd = sc.textFile(input_filepath)
intermediate_results = process_data(rdd)
support = float(sys.argv[2])
filter = float(sys.argv[1])
# Wrie the data to a CSV file
with open(intermediate_filepath, 'w') as f:
f.write("DATE-CUSTOMER_ID,PRODUCT_ID\n")
for line in intermediate_results:
f.write(','.join(line) + "\n")
rdd = preprocessing(intermediate_filepath,filter)
n_baskets = counting_baskets(rdd)
# SON ALGORITHM IMPLEMENTATION
# PHASE 1:
# Map -- Ouputs every frequent canidate from each bucket
# Ouputs every frequent canidate from each bucket
# Reduce -- This gets rid of all the different types of pairs and only keeps the frequent ones
# first pass of SON algorithm
candidates, can_results = son_pass_1(rdd, support, n_baskets)
# Second pass of son algorithm just checks if the canidates are actually frequent
# second pass of son algorithm
results = son_pass_2(rdd, candidates, support)
# Print the results
# print_results(results,'Frequent Itemsets:')
with open(output_filepath, 'w') as f:
# Write the candidates
write_results(can_results, 'Candidates:', f)
# Write the frequent itemsets
write_results(results, 'Frequent Itemsets:', f)
time_end = time.time()
print('Duration:', time_end-time_start)