Python PySpark-在没有显式会话密钥的情况下组合会话数据/迭代所有行

Python PySpark-在没有显式会话密钥的情况下组合会话数据/迭代所有行,python,apache-spark,pyspark,mapreduce,apache-spark-sql,Python,Apache Spark,Pyspark,Mapreduce,Apache Spark Sql,我试图在PySpark中聚合没有真正会话“键”的会话数据。我有在特定时间在某个区域检测到个人的数据,我想将其汇总为特定就诊期间在每个区域花费的时间(见下文)。 这里比较棘手的一点是,我想推断某人离开每个区域的时间与他们在下一个区域被检测到的时间相同。这意味着我需要使用下一个区域ID的开始时间作为任何给定区域ID的结束时间。对于同一个人,区域ID也可以显示多次 我在MapReduce中实现了这一点,我迭代所有行并聚合时间,直到检测到一个新的AreaID或个人,然后输出记录。在Spark中有没有类似

我试图在PySpark中聚合没有真正会话“键”的会话数据。我有在特定时间在某个区域检测到个人的数据,我想将其汇总为特定就诊期间在每个区域花费的时间(见下文)。

这里比较棘手的一点是,我想推断某人离开每个区域的时间与他们在下一个区域被检测到的时间相同。这意味着我需要使用下一个区域ID的开始时间作为任何给定区域ID的结束时间。对于同一个人,区域ID也可以显示多次

我在MapReduce中实现了这一点,我迭代所有行并聚合时间,直到检测到一个新的AreaID或个人,然后输出记录。在Spark中有没有类似的方法?有没有更好的方法来解决这个问题

同样值得注意的是,我不想输出记录,除非在另一个区域(例如,个人,下面的区域)检测到该个人

我有以下格式的数据集:

Individual AreaID Datetime of Detection IndividualX AreaQ 1/7/2015 0:00 IndividualX AreaQ 1/7/2015 1:00 IndividualX AreaW 1/7/2015 3:00 IndividualX AreaQ 1/7/2015 4:00 IndividualY AreaZ 2/7/2015 4:00 IndividualY AreaZ 2/7/2015 5:00 IndividualY AreaW 2/7/2015 6:00 IndividualY AreaT 2/7/2015 7:00 单个区域ID日期检测时间 IndividualX AreaQ 1/7/2015 0:00 IndividualX AreaQ 1/7/2015 1:00 个人X区W 1/7/2015 3:00 IndividualX AreaQ 1/7/2015 4:00 个人区域Z 2015年2月7日4:00 个人区域Z 2015年2月7日5:00 个人区域W 2015年2月7日6:00 个人区域2015年2月7日7:00 我希望得到以下所需的输出:

Individual AreaID Start_Time End_Time Duration (minutes) IndividualX AreaQ 1/7/2015 0:00 1/7/2015 3:00 180 IndividualX AreaW 1/7/2015 3:00 1/7/2015 4:00 60 IndividualY AreaZ 2/7/2015 4:00 2/7/2015 6:00 120 IndividualY AreaW 2/7/2015 6:00 2/7/2015 7:00 60 ['IndividualX|AreaQ|2015-07-01 00:00:00|2015-07-01 03:00:00|180.0', 'IndividualX|AreaW|2015-07-01 03:00:00|2015-07-01 04:00:00|60.0', 'IndividualY|AreaZ|2015-07-02 04:00:00|2015-07-02 06:00:00|120.0', 'IndividualY|AreaW|2015-07-02 06:00:00|2015-07-02 07:00:00|60.0'] 单个区域ID开始\u时间结束\u持续时间(分钟) IndividualX AreaQ 1/7/2015 0:00 1/7/2015 3:00 180 个人X区W 2015年7月1日3:00 2015年7月1日4:00 60 个人区域Z 2015年2月7日4:00 2015年7月2日6:00 120 个人区域W 2015年2月7日6:00 2015年7月2日7:00 60
这是一个特别漂亮的解决方案,但您可以使用数据帧和窗口函数。假设您的输入如下所示:

rdd = sc.parallelize([
    ("IndividualX", "AreaQ",  "1/7/2015 0:00"),
    ("IndividualX", "AreaQ",  "1/7/2015 1:00"),
    ("IndividualX", "AreaW",  "1/7/2015 3:00"),
    ("IndividualX", "AreaQ",  "1/7/2015 4:00"),
    ("IndividualY", "AreaZ",  "2/7/2015 4:00"),
    ("IndividualY", "AreaZ",  "2/7/2015 5:00"),
    ("IndividualY", "AreaW",  "2/7/2015 6:00"),
    ("IndividualY", "AreaT",  "2/7/2015 7:00")
])
(individual, (area_id, datetime))
首先,我们必须将其转换为数据帧:

from datetime import datetime
from pyspark.sql import Row
from pyspark.sql import HiveContext

sqlContext = HiveContext(sc)

row = Row("individual", "area_id", "datetime")
fmt = "%d/%m/%Y %H:%M"
df = rdd.map(lambda r: row(r[0], r[1], datetime.strptime(r[2], fmt))).toDF()
result = (tmp
    .withColumn("end_time", end_time)
    .where(f.col("end_time").isNotNull())
    .withColumn("duration", duration)
    .withColumnRenamed("datetime", "start_time"))
接下来,让我们定义一个窗口:

from pyspark.sql import functions as f
from pyspark.sql.window import Window

w = Window().partitionBy("individual").orderBy("datetime")
临时柱:

p_area_id = f.lag("area_id").over(w) # Previous area

ind =  f.sum((
    p_area_id.isNull() | # No previous observation
    (p_area_id != f.col("area_id")) # Area changed
).cast("integer")).over(w)
使用上面定义的指示器,我们可以选择该区域的最小访问时间戳:

tmp = (df
   .withColumn("ind", ind)
   .groupBy("individual", "area_id", "ind")
   .agg(f.min("datetime").alias("datetime"))
   .drop("ind"))
最后,我们可以定义目标列:

end_time = f.lead(f.col("datetime")).over(w)

duration = (
    f.col("end_time").cast("integer") - f.col("datetime").cast("integer")) / 60
并构建输出数据帧:

from datetime import datetime
from pyspark.sql import Row
from pyspark.sql import HiveContext

sqlContext = HiveContext(sc)

row = Row("individual", "area_id", "datetime")
fmt = "%d/%m/%Y %H:%M"
df = rdd.map(lambda r: row(r[0], r[1], datetime.strptime(r[2], fmt))).toDF()
result = (tmp
    .withColumn("end_time", end_time)
    .where(f.col("end_time").isNotNull())
    .withColumn("duration", duration)
    .withColumnRenamed("datetime", "start_time"))
和输出:

+-----------+-------+--------------------+--------------------+--------+
| individual|area_id|          start_time|            end_time|duration|
+-----------+-------+--------------------+--------------------+--------+
|IndividualX|  AreaQ|2015-07-01 00:00:...|2015-07-01 03:00:...|   180.0|
|IndividualX|  AreaW|2015-07-01 03:00:...|2015-07-01 04:00:...|    60.0|
|IndividualY|  AreaZ|2015-07-02 04:00:...|2015-07-02 06:00:...|   120.0|
|IndividualY|  AreaW|2015-07-02 06:00:...|2015-07-02 07:00:...|    60.0|
+-----------+-------+--------------------+--------------------+--------+
如果您喜欢普通RDD,您可以将其重塑为以下形状:

rdd = sc.parallelize([
    ("IndividualX", "AreaQ",  "1/7/2015 0:00"),
    ("IndividualX", "AreaQ",  "1/7/2015 1:00"),
    ("IndividualX", "AreaW",  "1/7/2015 3:00"),
    ("IndividualX", "AreaQ",  "1/7/2015 4:00"),
    ("IndividualY", "AreaZ",  "2/7/2015 4:00"),
    ("IndividualY", "AreaZ",  "2/7/2015 5:00"),
    ("IndividualY", "AreaW",  "2/7/2015 6:00"),
    ("IndividualY", "AreaT",  "2/7/2015 7:00")
])
(individual, (area_id, datetime))

接下来是groupByKey,并在本地执行所需的操作。

Zero323的解决方案非常有效,但也希望发布rdd实现。我认为这将有助于人们尝试将流式MapReduce转换为pyspark。我的实现基本上将键(在本例中为个人)映射到一个流值列表,流值列表将与该键(区域和时间)关联,然后在该列表上迭代以满足迭代组件的要求-其余的只是通过键和映射进行正常减少

from pyspark import SparkContext, SparkFiles, SparkConf from datetime import datetime conf = SparkConf() sc = SparkContext(conf=conf) rdd = sc.parallelize(["IndividualX|AreaQ|1/7/2015 0:00", "IndividualX|AreaQ|1/7/2015 1:00", "IndividualX|AreaW|1/7/2015 3:00", "IndividualX|AreaQ|1/7/2015 4:00", "IndividualY|AreaZ|2/7/2015 4:00", "IndividualY|AreaZ|2/7/2015 5:00", "IndividualY|AreaW|2/7/2015 6:00", "IndividualY|AreaT|2/7/2015 7:00"]) def splitReduce(x): y = x.split('|') return (str(y[0]),[[str(y[2]),str(y[1])]]) def resultSet(x): processlist = sorted(x[1], key=lambda x: x[0]) result = [] start_area = processlist[0][1] start_date = datetime.strptime(processlist[0][0], '%d/%m/%Y %H:%M') dur = 0 if len(processlist) > 1: for datearea in processlist[1::]: end_date = datetime.strptime(datearea[0],'%d/%m/%Y %H:%M') end_area = datearea[1] dur = (end_date-start_date).total_seconds()/60 if start_area != end_area: result.append([start_area,start_date,end_date,dur]) start_date = datetime.strptime(datearea[0], '%d/%m/%Y %H:%M') start_area = datearea[1] dur = 0 return (x[0],result) def finalOut(x): return str(x[0]) + '|' + str(x[1][0]) + '|' + str(x[1][1]) + '|' + str(x[1][2]) + '|' + str(x[1][3]) footfall = rdd\ .map(lambda x: splitReduce(x))\ .reduceByKey(lambda a, b: a + b)\ .map(lambda x: resultSet(x))\ .flatMapValues(lambda x: x)\ .map(lambda x: finalOut(x))\ .collect() print footfall 从pyspark导入SparkContext、SparkFiles、SparkConf 从日期时间导入日期时间 conf=SparkConf() sc=SparkContext(conf=conf) rdd=sc.parallelize([“IndividualX | AreaQ | 1/7/2015 0:00”, “IndividualX | AreaQ | 1/7/2015 1:00”, “IndividualX | AreaW | 1/7/2015 3:00”, “IndividualX | AreaQ | 1/7/2015 4:00”, “个人| AreaZ | 2/7/2015 4:00”, “个人|区域| 2/7/2015 5:00”, “个人| AreaW | 2/7/2015 6:00”, “个人|区域| 2/7/2015 7:00”]) def splitReduce(x): y=x.split(“|”) 返回(str(y[0]),[[str(y[2]),str(y[1])]] def结果集(x): processlist=已排序(x[1],key=lambda x:x[0]) 结果=[] start_area=processlist[0][1] start_date=datetime.strtime(进程列表[0][0],“%d/%m/%Y%H:%m”) dur=0 如果len(processlist)>1: 对于processlist[1::]中的datearea: end_date=datetime.strtime(日期区域[0],“%d/%m/%Y%H:%m”) 结束区域=日期区域[1] dur=(结束日期-开始日期)。总秒数()/60 如果启动_区域!=结束区: 结果.追加([开始\u区域,开始\u日期,结束\u日期,dur]) 开始日期=datetime.strtime(日期区域[0],“%d/%m/%Y%H:%m”) 开始区域=日期区域[1] dur=0 返回(x[0],结果) 最终定义(x): 返回str(x[0])+'|'+str(x[1][0])+'|'+str(x[1][1])+'|'+str(x[1][2])+'|'+str(x[1][3]) 落差=rdd\ .map(λx:splitReduce(x))\ .还原基(λa,b:a+b)\ .map(lambda x:resultSet(x))\ .flatmap值(λx:x)\ .map(lambda x:finalOut(x))\ .collect() 印花足迹 提供以下内容的输出:

Individual AreaID Start_Time End_Time Duration (minutes) IndividualX AreaQ 1/7/2015 0:00 1/7/2015 3:00 180 IndividualX AreaW 1/7/2015 3:00 1/7/2015 4:00 60 IndividualY AreaZ 2/7/2015 4:00 2/7/2015 6:00 120 IndividualY AreaW 2/7/2015 6:00 2/7/2015 7:00 60 ['IndividualX|AreaQ|2015-07-01 00:00:00|2015-07-01 03:00:00|180.0', 'IndividualX|AreaW|2015-07-01 03:00:00|2015-07-01 04:00:00|60.0', 'IndividualY|AreaZ|2015-07-02 04:00:00|2015-07-02 06:00:00|120.0', 'IndividualY|AreaW|2015-07-02 06:00:00|2015-07-02 07:00:00|60.0'] [IndividualX | AreaQ | 2015-07-01 00:00:00 | 2015-07-01 03:00:00 | 180.0', “IndividualX | AreaW | 2015-07-01 03:00:00 | 2015-07-01 04:00:00 | 60.0”, “个人|区域| 2015-07-02 04:00:00 | 2015-07-02 06:00:00 | 120.0”, “个人|区域| 2015-07-02 06:00:00 | 2015-07-02 07:00:00 | 60.0”]
请发布您迄今为止尝试过的代码。甚至不知道如何使用Spark实现它。我可以发布MapReduce代码,但不确定这会有多大帮助,因为它会在记录流化时处理记录。感谢您的解决方案!我也有一个工作的RDD实现,我将在下面很快发布。