У меня есть несколько кадров данных со следующей структурой:
ID| Page | User | Timestamp |
|1|Page 1 |Ericd |2002-09-07 19:39:55|
|1|Page 1 |Liir |2002-10-12 03:01:42|
|1|Page 1 |Tubby |2002-10-12 03:02:23|
|1|Page 1 |Mojo |2002-10-12 03:18:24|
|1|Page 1 |Kirf |2002-10-12 03:19:03|
|2|Page 2 |The Epopt |2001-11-28 22:27:37|
|2|Page 2 |Conversion script|2002-02-03 01:49:16|
|2|Page 2 |Bryan Derksen |2002-02-25 16:51:15|
|2|Page 2 |Gear |2002-10-04 12:46:06|
|2|Page 2 |Tim Starling |2002-10-06 08:13:42|
|2|Page 2 |Tim Starling |2002-10-07 03:00:54|
|2|Page 2 |Salsa Shark |2003-03-18 01:45:32|
и я хотел бы найти количество пользователей, которые посетили страницы в течение некоторого периода времени (например, за каждый месяц). Например, за 10-й месяц 2002 года результат будет
|1|Page 1 |Liir |2002-10-12 03:01:42|
|1|Page 1 |Tubby |2002-10-12 03:02:23|
|1|Page 1 |Mojo |2002-10-12 03:18:24|
|1|Page 1 |Kirf |2002-10-12 03:19:03|
|2|Page 2 |Gear |2002-10-04 12:46:06|
|2|Page 2 |Tim Starling |2002-10-06 08:13:42|
|2|Page 2 |Tim Starling |2002-10-07 03:00:54|
и количество страниц:
numberOfUsers (in October 2002)
|1|Page 1 | 4
|2|Page 2 | 3
Вопрос заключается в том, как применять эту логику для каждого месяца в течение каждого года. Я выяснил, как найти, например, вхождения в течение последних n-дней
days = lambda i: i * 86400
window = (Window().partitionBy(col("page"))
.orderBy(col("timestamp").cast("timestamp").cast("long")).rangeBetween(-days(30), 0))
df = df.withColumn("monthly_occurrences", func.count("user").over(window))
df.show()
некоторые предложения, которые я ценю
Сначала можно создать столбец, который содержит комбинацию год-месяц, а затем использовать этот столбец для группировки. Таким образом, рабочий пример:
import pyspark.sql.functions as F
df = sc.parallelize([
('2018-06-02T00:00:00.000Z','tim', 'page 1' ),
('2018-07-20T00:00:00.000Z','tim', 'page 1' ),
('2018-07-20T00:00:00.000Z','john', 'page 2' ),
('2018-07-20T00:00:00.000Z','john', 'page 2' ),
('2018-08-20T00:00:00.000Z','john', 'page 2' )
]).toDF(("datetime","user","page" ))
df = df.withColumn('yearmonth',F.concat(F.year('datetime'),F.lit('-'),F.month('datetime')))
df_agg = df.groupBy('yearmonth','page').count()
df_agg.show()
Выход:
+---------+------+-----+
|yearmonth| page|count|
+---------+------+-----+
| 2018-7|page 2| 2|
| 2018-6|page 1| 1|
| 2018-7|page 1| 1|
| 2018-8|page 2| 1|
+---------+------+-----+
Надеюсь это поможет!
Если вы ищете динамические периоды, сначала конвертируйте дату в метку времени, затем выровняйте все временные метки с сегодняшнего дня и разделите (целое число) на временную метку временного интервала, который вы хотите сгруппировать. Приведенный ниже код группирует строки на 5-дневные интервалы.
import pyspark.sql.functions as F
from datetime import datetime
# todays timestamp
Today = datetime.today().timestamp()
# how many timestamp is today
DAY_TIMESTAMPS = 24 * 60 * 60
df = sc.parallelize([
('2017-06-02 00:00:00','tim', 'page 1' ),
('2017-07-20 00:00:00','tim', 'page 1' ),
('2017-07-21 00:00:00','john', 'page 2' ),
('2017-07-22 00:00:00','john', 'page 2' ),
('2017-08-23 00:00:00','john', 'page 2' )
]).toDF(("datetime","user","page" ))
# group by five days
timeInterval = 5* DAY_TIMESTAMPS
df \
.withColumn('timestamp', F.unix_timestamp(F.to_date('datetime', 'yyyy-MM-dd HH:mm:ss'))) \
.withColumn('timeIntervalBefore', ((Today-F.col('timestamp'))/(timeInterval)).cast('integer')) \
.groupBy('timeIntervalBefore', 'page') \
.agg(F.count('user').alias('number of users')).show()
Результаты:
+------------------+------+---------------+
|timeIntervalBefore| page|number of users|
+------------------+------+---------------+
| 70|page 2| 2|
| 80|page 1| 1|
| 70|page 1| 1|
| 64|page 2| 1|
+------------------+------+---------------+
Если вам нужно приблизиться к датам периодов времени:
df \
.withColumn('timestamp', F.unix_timestamp(F.to_date('datetime', 'yyyy-MM-dd HH:mm:ss'))) \
.withColumn('timeIntervalBefore', ((Today-F.col('timestamp'))/(timeInterval)).cast('integer')) \
.groupBy('timeIntervalBefore', 'page') \
.agg(
F.count('user').alias('number_of_users'),
F.min('timestamp').alias('FirstDay'),
F.max('timestamp').alias('LastDay')) \
.select(
'page',
'number_of_users',
F.from_unixtime('firstday').alias('firstDay'),
F.from_unixtime('firstday').alias('lastDay')).show()
Результаты:
+------+---------------+-------------------+-------------------+
| page|number_of_users| firstDay| lastDay|
+------+---------------+-------------------+-------------------+
|page 2| 2|2017-07-21 00:00:00|2017-07-21 00:00:00|
|page 1| 1|2017-06-02 00:00:00|2017-06-02 00:00:00|
|page 1| 1|2017-07-20 00:00:00|2017-07-20 00:00:00|
|page 2| 1|2017-08-23 00:00:00|2017-08-23 00:00:00|
+------+---------------+-------------------+-------------------+