JaysonSunshine

Spark code

Apr 8th, 2016
170
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.78 KB | None | 0 0
  1. from pyspark.sql.types import *
  2. from pyspark.sql.functions import *
  3. import json, csv
  4.  
  5. from pyspark import SparkContext, SparkConf
  6. from pyspark.sql import SQLContext
  7.  
  8. #looks in the local directory for the AWS credentials
  9. with open("credentials.csv") as f:
  10. access_key_id, secret_access_key = f.readlines()[1].split(',')[1:]
  11.  
  12. sc = SparkContext()
  13.  
  14. #this approach may not be optimal, as it's accessing a 'private' member of sc. The other approaches of modifying conf/spark-env.sh or including it in the s3 URL seem to have some security problems
  15. sc._jsc.hadoopConfiguration().set("fs.s3n.awsAccessKeyId", access_key_id)
  16. sc._jsc.hadoopConfiguration().set("fs.s3n.awsSecretAccessKey", secret_access_key)
  17. sqlContext = SQLContext(sc)
  18.  
  19. #load users data
  20. users = sc.textFile("s3n://ds12-education/clickstream-challenge/data/users.tsv.gz")
  21. users_header = users.first()
  22. users = users.filter(lambda elem: elem != users_header).map(lambda elem: elem.split('\t')).cache()
  23.  
  24. #load prodocuts data
  25. products = sc.textFile("s3n://ds12-education/clickstream-challenge/data/products.tsv.gz")
  26. products_header = products.first()
  27. products = products.filter(lambda elem: elem != products_header).map(lambda elem: elem.split('\t')).cache()
  28.  
  29. #You can argue this is a little clunky, but its a good framework if we needed to do multiple, unique transformations on the clickstream columns. In the present case we are merely subsetting the columns and doing a transformation on a single column
  30. def clickstream_columns(elem):
  31. output_columns = list()
  32. #we're going to subset some of the columns for clickstream
  33. columns = [1, 7, 12, 13, 49, 50, 52] #"timestamp ip_address url swid city country state"
  34. for column in columns:
  35. if column == 13: #additionally, clickstream.swid,c13, has beginning and ending braces not found in users.swid
  36. output_columns.append(elem[column].strip('{}'))
  37. else:
  38. output_columns.append(elem[column])
  39. return output_columns
  40.  
  41. #load click data
  42. clickstream = sc.textFile("s3n://ds12-education/clickstream-challenge/data/Omniture*").map(lambda elem: elem.split('\t')).map(clickstream_columns).cache()
  43.  
  44. #exploratory data analysis
  45. #users.count() #38,455
  46. #products.count() #31
  47. #clickstream.count() #421,266
  48.  
  49. #now we create DataFrames from the above RDDs
  50. users_schemaString = "swid birth_dt gender_cd"
  51. products_schemaString = "url category"
  52. clickstream_schemaString = "timestamp ip_address url swid city country state"
  53.  
  54. def rdd_to_dataframe(rdd, schemaString):
  55. fields = [StructField(field_name, StringType(), True) for field_name in schemaString.split()]
  56. schema = StructType(fields)
  57. return sqlContext.createDataFrame(rdd, schema)
  58.  
  59. users_df = rdd_to_dataframe(users, users_schemaString)
  60. products_df = rdd_to_dataframe(products, products_schemaString)
  61. clickstream_df = rdd_to_dataframe(clickstream, clickstream_schemaString)
  62.  
  63. #to answer the questions of interest, we must join the datasets
  64. click_products = clickstream_df.join(products_df, 'url').cache()
  65. all_data = click_products.join(users_df, 'swid', how='left_outer').cache()
  66.  
  67. #distinct users and total number of clicks per day
  68. groupedData = all_data.groupBy(year(all_data.timestamp), month(all_data.timestamp), dayofmonth(all_data.timestamp))
  69. clicks_per_day = groupedData.count()
  70. unique_users_per_day = groupedData.agg(countDistinct('swid'))
  71. clicks_distint_users = unique_users_per_day.join(clicks_per_day, ['year(timestamp)', 'month(timestamp)', 'dayofmonth(timestamp)'])
  72.  
  73. #total number of clicks per product category
  74. clicks_per_product = click_products.groupBy('category').count()
  75.  
  76. #generate TSV files
  77. with open('clicks_per_product.tsv', 'wb') as tsvfile:
  78. tsvwriter = csv.writer(tsvfile, delimiter='\t')
  79. tsvwriter.writerow(['category', 'count'])
  80. tsvwriter.writerows(clicks_per_product.collect())
  81.  
  82. with open('clicks_distint_users.tsv', 'wb') as tsvfile:
  83. tsvwriter = csv.writer(tsvfile, delimiter='\t')
  84. tsvwriter.writerow(['year', 'month', 'day', 'distinct_users', 'clickcount'])
  85. tsvwriter.writerows(clicks_distint_users.collect())
  86.  
  87. #the following generates the JSON of interest; fundamentally we use the groupByKey method for PairRDDs to generate the collection of clicks for each user
  88. nested_rdd = all_data.rdd.map(lambda elem: ((elem.swid, elem.birth_dt, elem.gender_cd), {'url': elem.url, 'timestamp': elem.timestamp, 'ipAddress': elem.ip_address, 'city': elem.city, 'state': elem.state, 'country': elem.country, 'category': elem.category})).groupByKey().map(lambda (key, value): json.dumps({'UserId': key[0], 'birthDate': key[1], 'genderCd': key[2], 'clicks': list(value)}))
  89.  
  90. #the default case is not to coalesce before writing the output, especially if the dataset is large. In this case, the instructions said to produce a "JSON file" and the data-size is plausible for a single file ;)
  91. nested_rdd.coalesce(1).saveAsTextFile('json_single_output')
Advertisement
Add Comment
Please, Sign In to add comment