SessionEncoder#
- class skrub.SessionEncoder(timestamp_col, split_by=None, session_gap=1800, suffix='session_id')[source]#
Add a session ID column to a dataframe based on time gaps and other columns.
A session is defined as a sequence of events where consecutive events are separated by at most
session_gapseconds. Additionally, it is possible to provide a column or list of columns that can be used to distinguish between sessions, such as user identifiers (specified by thesplit_bycolumn). Within each sequence identified by a unique value in thesplit_bycolumn(s), a new session is started when the time gap between events exceedssession_gapseconds.The encoder takes care of grouping the data by
split_byand sorting by timestamp column before identifying sessions, and sorting it back to the original order at the end, so the original order of events in the input dataframe does not matter.If a null value is present in the timestamp column or any of the
split_bycolumns, the corresponding row will be assigned a session ID of -1, and will be ignored when computing time intervals.All unrelated columns are passed through unchanged.
- Parameters:
- timestamp_col
str The name of the column that identifies the time of an event. This column is used to determine the start and end of a session.
timestamp_colmust be a datetime, and an error will be raised otherwise. Sessions are defined within each group of events that have the same value in thesplit_bycolumn(s) (or all events ifsplit_byis None), and the result will be correct no matter the order of the rows in the input dataframe.- split_byoptional[
str,list[str]], default=None The name of the column (typically the user ID), or list of columns, that is used to identify independent event sequence (such as the activity of different users). A session boundary is created when the value in any of these columns changes, or when the time gap between events exceeds
session_gap. This is typically a user identifier column, but it can also be used to define sessions by other groupings (e.g. user and device type). If not provided, sessions are detected based on the time gap between events, and all events are considered to belong to the same user (or group).- session_gap
int, default=1800 The maximum gap (in seconds) between events in a session. If the gap between two events exceeds this value, they are considered to be in different sessions. Default is 1800 seconds (30 minutes).
- suffix
str, default=”session_id” The suffix to be added to the name of the created session id column.
- timestamp_col
- Attributes:
- all_inputs_
listofstr All column names in the input dataframe.
- all_outputs_: list of str
All column names in the input dataframe plus the new column that identifies the session, with name “{timestamp}_{suffix}”.
- session_id_column_
str The name of the session ID column that is added to the dataframe. This is generated as “{timestamp_col}_{suffix}”, but if this name already exists in the input dataframe, a random suffix is added to avoid overwriting it.
- all_inputs_
Examples
Consider this example where we have a dataframe with user events, and we want to identify sessions based on a 30-minute gap between events for each user. Users are identified by the value of the column
user_id. Note that the order of the rows in the input dataframe does not matter.Sessions are defined by sorting over the ``split_by``columns (if provided) and then by the timestamp.
>>> import pandas as pd >>> from datetime import datetime, timedelta >>> data = { ... "user_id": [1, 1, 1, 1, 1, 2, 2], ... "device_id": [ ... "mobile", ... "mobile", ... "desktop", ... "desktop", ... "mobile", ... "mobile", ... "mobile", ... ], ... "timestamp": [ ... pd.Timestamp("2024-01-01 10:00:00"), ... pd.Timestamp("2024-01-01 10:10:00"), # 10 min later, same session ... pd.Timestamp("2024-01-01 10:05:00"), # Different device (sorted), ... # different session ... pd.Timestamp("2024-01-01 10:20:00"), # 15 min later, same session ... # different session ... pd.Timestamp("2024-01-01 11:20:00"), # 60 min later, new session ... pd.Timestamp("2024-01-01 10:00:00"), # Different user ... pd.Timestamp("2024-01-01 10:15:00"), # 15 min later, same session ... ], ... "action": [ ... "view", ... "purchase", ... "view", ... "add_to_cart", ... "checkout", ... "view", ... "wishlist", ... ], ... } >>> df = pd.DataFrame(data) >>> df user_id device_id timestamp action 0 1 mobile 2024-01-01 10:00:00 view 1 1 mobile 2024-01-01 10:10:00 purchase 2 1 desktop 2024-01-01 10:05:00 view 3 1 desktop 2024-01-01 10:20:00 add_to_cart 4 1 mobile 2024-01-01 11:20:00 checkout 5 2 mobile 2024-01-01 10:00:00 view 6 2 mobile 2024-01-01 10:15:00 wishlist
We use the
SessionEncoderwith defaultsession_gapof 30 minutes:>>> from skrub import SessionEncoder >>> encoder = SessionEncoder( ... split_by='user_id', timestamp_col='timestamp' ... ) >>> result = encoder.fit_transform(df) >>> result user_id device_id timestamp action timestamp_session_id 0 1 mobile 2024-01-01 10:00:00 view 0 1 1 mobile 2024-01-01 10:10:00 purchase 0 2 1 desktop 2024-01-01 10:05:00 view 0 3 1 desktop 2024-01-01 10:20:00 add_to_cart 0 4 1 mobile 2024-01-01 11:20:00 checkout 1 5 2 mobile 2024-01-01 10:00:00 view 2 6 2 mobile 2024-01-01 10:15:00 wishlist 2
In this example, grouping by
user_idresults in three separate sessions:User 1 has two sessions (session 0 and session 1) because there is a gap of 60 minutes between their events at 10:20 and 11:20, which exceeds the 30-minute threshold. The first four events of user 1 belong to session 0, while the last event belongs to session 1.
User 2 has one session (session 2) because all their events are within 30 minutes of the previous one.
You can also identify users by multiple columns. For instance, the same user on different devices should have separate sessions.
>>> encoder_multi = SessionEncoder( ... split_by=['user_id', 'device_id'], ... timestamp_col='timestamp', ... ) >>> result_multi = encoder_multi.fit_transform(df) >>> result_multi user_id device_id timestamp action timestamp_session_id 0 1 mobile 2024-01-01 10:00:00 view 1 1 1 mobile 2024-01-01 10:10:00 purchase 1 2 1 desktop 2024-01-01 10:05:00 view 0 3 1 desktop 2024-01-01 10:20:00 add_to_cart 0 4 1 mobile 2024-01-01 11:20:00 checkout 2 5 2 mobile 2024-01-01 10:00:00 view 3 6 2 mobile 2024-01-01 10:15:00 wishlist 3
In this example:
User 1 on “desktop” has session 0.
User 1 on “mobile” has two sessions, session 1 and session 2, because there is a gap of 60 minutes between their events at 10:10 and 11:20, which exceeds the 30-minute threshold.
User 2 on “mobile” has session 3 (different user).
Note that the value of the session IDs are arbitrary and depend on the order of events in the dataframe. The important thing is that events that belong to the same session have the same session ID, and events that belong to different sessions have different session IDs.
You can also use
SessionEncoderwithout a user identifier column. In this case, sessions are separated only by time gaps. This is useful for analyzing a single timeseries or events that don’t have a user dimension:>>> encoder_no_split = SessionEncoder( ... split_by=None, ... timestamp_col='timestamp', ... ) >>> data_no_split = { ... 'timestamp': [ ... pd.Timestamp('2024-01-01 10:00:00'), ... pd.Timestamp('2024-01-01 10:10:00'), # 10 min gap ... pd.Timestamp('2024-01-01 10:15:00'), # 5 min gap, still in session ... pd.Timestamp('2024-01-01 11:00:00'), # 45 min gap, new session ... pd.Timestamp('2024-01-01 11:10:00'), # 10 min gap, continue session ... ], ... 'event_type': ['start', 'action', 'action', 'restart', 'action'] ... } >>> df_no_split = pd.DataFrame(data_no_split) >>> result_no_split = encoder_no_split.fit_transform(df_no_split) >>> result_no_split timestamp event_type timestamp_session_id 0 2024-01-01 10:00:00 start 0 1 2024-01-01 10:10:00 action 0 2 2024-01-01 10:15:00 action 0 3 2024-01-01 11:00:00 restart 1 4 2024-01-01 11:10:00 action 1
In this example:
Events at 10:00, 10:10, and 10:15 form session 0 (all gaps < 30 min).
The event at 11:00 starts a new session 1 (45 min gap > 30 min).
The event at 11:10 continues session 1 (10 min gap < 30 min).
It is possible to change the duration of the session gap by setting the
session_gapparameter. For example, we can set it to 5 minutes (300 seconds) instead of the default 30 minutes, and this will change the session assignments accordingly:>>> encoder_new_gap = SessionEncoder( ... split_by=None, ... timestamp_col='timestamp', ... session_gap=300 ... ) >>> result_new_gap = encoder_new_gap.fit_transform(df_no_split) >>> result_new_gap timestamp event_type timestamp_session_id 0 2024-01-01 10:00:00 start 0 1 2024-01-01 10:10:00 action 1 2 2024-01-01 10:15:00 action 1 3 2024-01-01 11:00:00 restart 2 4 2024-01-01 11:10:00 action 3
It is also possible to change the suffix that is added at the end of the session ID column via the “suffix” parameter. This is useful, for example, if you want to add sessions based on different groupings or intervals:
>>> data_multi = { ... 'user_id': [1, 1, 1, 1, 2, 2], ... 'device_id': ['mobile', 'mobile', 'desktop', 'desktop', 'mobile', 'mobile'], ... 'timestamp': [ ... pd.Timestamp('2024-01-01 10:00:00'), ... pd.Timestamp('2024-01-01 10:10:00'), # 10 min later, same session ... pd.Timestamp('2024-01-01 10:05:00'), # Different device (sorted), ... # different session ... pd.Timestamp('2024-01-01 10:20:00'), # 15 min later, same session ... pd.Timestamp('2024-01-01 10:00:00'), # Different user ... pd.Timestamp('2024-01-01 10:15:00'), # 15 min later, same session ... ], ... 'action': ['view', 'purchase', 'view', 'checkout', 'login', 'view'] ... } >>> df = pd.DataFrame(data_multi) >>> encoder_user = SessionEncoder("timestamp", ... split_by=["user_id"], suffix="user") >>> encoder_user.fit_transform(df) user_id device_id timestamp action timestamp_user 0 1 mobile 2024-01-01 10:00:00 view 0 1 1 mobile 2024-01-01 10:10:00 purchase 0 2 1 desktop 2024-01-01 10:05:00 view 0 3 1 desktop 2024-01-01 10:20:00 checkout 0 4 2 mobile 2024-01-01 10:00:00 login 1 5 2 mobile 2024-01-01 10:15:00 view 1
>>> encoder_user_device = SessionEncoder("timestamp", ... split_by=["user_id", "device_id"], ... suffix="user_device") >>> encoder_user_device.fit_transform(df) user_id device_id timestamp action timestamp_user_device 0 1 mobile 2024-01-01 10:00:00 view 1 1 1 mobile 2024-01-01 10:10:00 purchase 1 2 1 desktop 2024-01-01 10:05:00 view 0 3 1 desktop 2024-01-01 10:20:00 checkout 0 4 2 mobile 2024-01-01 10:00:00 login 2 5 2 mobile 2024-01-01 10:15:00 view 2
When the timestamp column or any of the split_by columns contains null values, those rows will be assigned session ID -1, and will be ignored when computing time intervals. In some versions of pandas, adding nulls to a pandas column of integers will convert it to float, which can cause issues with grouping if there are a lot of sessions.
>>> data_with_nulls = { ... 'user_id': [1, 1, None, 1], # None value in split_by column ... 'timestamp': [ ... pd.Timestamp('2024-01-01 10:00:00'), ... None, # None value in timestamp column ... pd.Timestamp('2024-01-01 10:10:00'), ... pd.Timestamp('2024-01-01 10:20:00'), ... ], ... } >>> df_with_nulls = pd.DataFrame(data_with_nulls) >>> encoder_with_nulls = SessionEncoder( ... split_by='user_id', ... timestamp_col='timestamp' ... ) >>> result_with_nulls = encoder_with_nulls.fit_transform(df_with_nulls) >>> result_with_nulls user_id timestamp timestamp_session_id 0 1.0 2024-01-01 10:00:00 0 1 1.0 NaT -1 2 NaN 2024-01-01 10:10:00 -1 3 1.0 2024-01-01 10:20:00 0
In this example:
Row 0 has valid user_id and timestamp, so it gets session ID 0.
Row 1 has a null timestamp, so it gets session ID -1.
Row 2 has a null user_id, so it gets session ID -1.
Row 3 has valid user_id and timestamp, and since there is less than 30 minutes between rows 0 and 3 (ignoring row 1 and 2 due to null values), it gets the same session ID as row 0.
Methods
fit(X[, y])Fit the transformer to the data.
fit_transform(X[, y])Fit the transformer to the data and return the transformed dataframe.
get_feature_names_out([input_features])Return the column names of the output of
transformas a list of strings.get_params([deep])Get parameters for this estimator.
set_output(*[, transform])Set output container.
set_params(**params)Set the parameters of this estimator.
transform(X[, y])Transform the data by encoding sessions.
- fit(X, y=None)[source]#
Fit the transformer to the data.
- Parameters:
- Xpandas.DataFrame or polars.DataFrame
The input dataframe.
- y
None Ignored.
- Returns:
- selfSessionEncoder
The fitted transformer.
- fit_transform(X, y=None)[source]#
Fit the transformer to the data and return the transformed dataframe.
- Parameters:
- Xpandas.DataFrame or polars.DataFrame
The input dataframe.
- y
None Ignored.
- Returns:
- pandas.DataFrame or polars.DataFrame
The transformed dataframe with session information.
- get_feature_names_out(input_features=None)[source]#
Return the column names of the output of
transformas a list of strings.- Parameters:
- input_featuresarray_like of
strorNone, default=None Ignored.
- input_featuresarray_like of
- Returns:
listof stringsThe column names.
- set_output(*, transform=None)[source]#
Set output container.
Refer to the user guide for more details and Introducing the set_output API for an example on how to use the API.
- Parameters:
- transform{“default”, “pandas”, “polars”}, default=None
Configure output of
transformandfit_transform."default": Default output format of a transformer"pandas": DataFrame output"polars": Polars outputNone: Transform configuration is unchanged
Added in version 1.4:
"polars"option was added.
- Returns:
- selfestimator instance
Estimator instance.
- set_params(**params)[source]#
Set the parameters of this estimator.
The method works on simple estimators as well as on nested objects (such as
Pipeline). The latter have parameters of the form<component>__<parameter>so that it’s possible to update each component of a nested object.- Parameters:
- **params
dict Estimator parameters.
- **params
- Returns:
- selfestimator instance
Estimator instance.
Gallery examples#
Sessions in time-based data: Predicting user purchases with the SessionEncoder