我遇到的问题是,API的解决方案对我来说并不明显,我想知道最有效的方法是什么。
用例:
我收集了两种类型的推文并将其存储在数据框中(我们称之为 Type1 , Type2 )。对于这个问题,重要的是它发布的 TimeStamp 。对于 Type1 的每条推文,我需要获取 Type2 的所有推文,这些推文属于某个+ / - Type1的TimeStamp周围的 时间窗口 ,并计算一个度量,该度量取决于每个 Type2 推文在此时间窗口内的时间增量。
根据我的尝试,TimeStamp列上的一个简单的 WindowSpec 在这里不起作用。如果我这样做,我会为每个时间戳获得一个windowSpec,但我只需要一些时间戳(Type1s)。我尝试为每种类型的推文创建两个TimeStamp列,并按Type1时间戳排序。但是“rangesBetween”选项似乎只适用于我排序的列(我需要排序Type1-Timestamp列,但rangeBetween Type2之间) -Timestamp列。)
我想出的解决方案是创建2个数据帧,每种类型一个。然后,对于 Type1 推文,我将收集到所有时间戳的列表中,并且对于每个 Type2 推文,我计算时间差值此列表中的每个时间戳都使用它。这可行,但解决方案对我来说效率低下,而且对于足够大的数据帧,收集过程可能会因内存问题而失败。
希望我对问题描述很清楚:)
答案 0 :(得分:1)
这些可能会重叠窗口,因此dat <-
structure(list(A = c(1L, 5L), B = c(2L, 7L), C = c(3L, 9L)), .Names = c("A",
"B", "C"), class = "data.frame", row.names = c(NA, -2L))
dat1 <-
structure(list(A = 1:4), .Names = "A", row.names = c(NA, -4L),
class = "data.frame")
无法实现,并且帧不能“动态”(它们不能基于当前行的时间戳值)。
在我看来,您应该使用partitionBy
流式传输Twitter API并对批次执行操作。但是,既然你已经拥有了数据帧,那么我们就试试吧。
收集值不是正确的解决方案(它会将所有内容都带到驱动程序的内存中)。另一种可能性是做一个非常昂贵的笛卡尔连接,你最终会在最后的操作中过滤很多行。我们可以通过创建自己的动态批次来解决问题。
首先让我们为类型推文1和2创建两个示例数据帧:
Kafka
假设我们的窗口应为+/- import datetime as dt
import numpy as np
np.random.seed(0)
time_deltas1 = np.random.randint(0, 10, 5).cumsum()
time_deltas2 = np.random.randint(0, 10, 20).cumsum()
df1 = spark.createDataFrame(
sc.parallelize(zip(
[dt.datetime(2017,1,1,0,0,0) + dt.timedelta(minutes=4*int(x)) for x in time_deltas1],
[chr(c) for c in range(ord('a'), ord('f'))]
)),
["ts1", "text1"]
)
df2 = spark.createDataFrame(
sc.parallelize(zip(
[dt.datetime(2017,1,1,0,0,0) + dt.timedelta(minutes=int(x)) + dt.timedelta(seconds = 30) for x in time_deltas2],
[chr(c) for c in range(ord('f'), ord('z'))]
)),
["ts2", "text2"]
)
+-------------------+-----+
| ts1|text1|
+-------------------+-----+
|2017-01-01 00:20:00| a|
|2017-01-01 00:20:00| b|
|2017-01-01 00:32:00| c|
|2017-01-01 00:44:00| d|
|2017-01-01 01:12:00| e|
+-------------------+-----+
+-------------------+-----+
| ts2|text2|
+-------------------+-----+
|2017-01-01 00:09:30| f|
|2017-01-01 00:12:30| g|
|2017-01-01 00:17:30| h|
|2017-01-01 00:19:30| i|
|2017-01-01 00:23:30| j|
|2017-01-01 00:30:30| k|
|2017-01-01 00:36:30| l|
|2017-01-01 00:44:30| m|
|2017-01-01 00:52:30| n|
|2017-01-01 00:53:30| o|
|2017-01-01 00:59:30| p|
|2017-01-01 01:06:30| q|
|2017-01-01 01:13:30| r|
|2017-01-01 01:21:30| s|
|2017-01-01 01:22:30| t|
|2017-01-01 01:27:30| u|
|2017-01-01 01:36:30| v|
|2017-01-01 01:44:30| w|
|2017-01-01 01:53:30| x|
|2017-01-01 01:57:30| y|
+-------------------+-----+
分钟,因此我们需要n
分钟窗口。
首先,我们将2*n
转换为timestamps
然后我们将它们四舍五入到最接近int
分钟的两个时间戳:
2*n
将unix时间戳转换为时间戳以显示:
import pyspark.sql.functions as psf
n = 5*60 # for +/- 5 minutes
df1 = df1.withColumn("ts1", psf.unix_timestamp(df1.ts1)).withColumn(
"time_range",
psf.explode(psf.array((psf.col("ts1")/(2*n)).cast("int") * (2*n), ((1 + psf.col("ts1")/(2*n)).cast("int")) * (2*n)))
)
df2 = df2.withColumn("ts2", psf.unix_timestamp(df2.ts2)).withColumn(
"time_range",
psf.explode(psf.array((psf.col("ts2")/(2*n)).cast("int") * (2*n), ((1 + psf.col("ts2")/(2*n)).cast("int")) * (2*n)))
)
我们现在可以加入2个数据帧,最终表将小于笛卡尔连接表,并在 +-------------------+-----+-------------------+
| ts1|text1| time_range|
+-------------------+-----+-------------------+
|2017-01-01 00:20:00| a|2017-01-01 00:20:00|
|2017-01-01 00:20:00| a|2017-01-01 00:30:00|
|2017-01-01 00:20:00| b|2017-01-01 00:20:00|
|2017-01-01 00:20:00| b|2017-01-01 00:30:00|
|2017-01-01 00:32:00| c|2017-01-01 00:30:00|
|2017-01-01 00:32:00| c|2017-01-01 00:40:00|
|2017-01-01 00:44:00| d|2017-01-01 00:40:00|
|2017-01-01 00:44:00| d|2017-01-01 00:50:00|
|2017-01-01 01:12:00| e|2017-01-01 01:10:00|
|2017-01-01 01:12:00| e|2017-01-01 01:20:00|
+-------------------+-----+-------------------+
+-------------------+-----+-------------------+
| ts2|text2| time_range|
+-------------------+-----+-------------------+
|2017-01-01 00:09:30| f|2017-01-01 00:00:00|
|2017-01-01 00:09:30| f|2017-01-01 00:10:00|
|2017-01-01 00:12:30| g|2017-01-01 00:10:00|
|2017-01-01 00:12:30| g|2017-01-01 00:20:00|
|2017-01-01 00:17:30| h|2017-01-01 00:10:00|
|2017-01-01 00:17:30| h|2017-01-01 00:20:00|
|2017-01-01 00:19:30| i|2017-01-01 00:10:00|
|2017-01-01 00:19:30| i|2017-01-01 00:20:00|
|2017-01-01 00:23:30| j|2017-01-01 00:20:00|
|2017-01-01 00:23:30| j|2017-01-01 00:30:00|
|2017-01-01 00:30:30| k|2017-01-01 00:30:00|
|2017-01-01 00:30:30| k|2017-01-01 00:40:00|
|2017-01-01 00:36:30| l|2017-01-01 00:30:00|
|2017-01-01 00:36:30| l|2017-01-01 00:40:00|
|2017-01-01 00:44:30| m|2017-01-01 00:40:00|
|2017-01-01 00:44:30| m|2017-01-01 00:50:00|
|2017-01-01 00:52:30| n|2017-01-01 00:50:00|
|2017-01-01 00:52:30| n|2017-01-01 01:00:00|
|2017-01-01 00:53:30| o|2017-01-01 00:50:00|
|2017-01-01 00:53:30| o|2017-01-01 01:00:00|
|2017-01-01 00:59:30| p|2017-01-01 00:50:00|
|2017-01-01 00:59:30| p|2017-01-01 01:00:00|
|2017-01-01 01:06:30| q|2017-01-01 01:00:00|
|2017-01-01 01:06:30| q|2017-01-01 01:10:00|
|2017-01-01 01:13:30| r|2017-01-01 01:10:00|
|2017-01-01 01:13:30| r|2017-01-01 01:20:00|
|2017-01-01 01:21:30| s|2017-01-01 01:20:00|
|2017-01-01 01:21:30| s|2017-01-01 01:30:00|
|2017-01-01 01:22:30| t|2017-01-01 01:20:00|
|2017-01-01 01:22:30| t|2017-01-01 01:30:00|
|2017-01-01 01:27:30| u|2017-01-01 01:20:00|
|2017-01-01 01:27:30| u|2017-01-01 01:30:00|
|2017-01-01 01:36:30| v|2017-01-01 01:30:00|
|2017-01-01 01:36:30| v|2017-01-01 01:40:00|
|2017-01-01 01:44:30| w|2017-01-01 01:40:00|
|2017-01-01 01:44:30| w|2017-01-01 01:50:00|
|2017-01-01 01:53:30| x|2017-01-01 01:50:00|
|2017-01-01 01:53:30| x|2017-01-01 02:00:00|
|2017-01-01 01:57:30| y|2017-01-01 01:50:00|
|2017-01-01 01:57:30| y|2017-01-01 02:00:00|
+-------------------+-----+-------------------+
上过滤:
|ts1 - ts2| <= n min
将时间戳转换回时间戳格式后,我们可以收集类型2的不同推文:
df = df1.join(df2, "time_range").filter(
(psf.abs(df1.ts1 - df2.ts2) <= n) | (psf.isnull(df2.ts2))
).withColumn("ts1", psf.from_unixtime("ts1")).withColumn("ts2", psf.from_unixtime("ts2"))