Bitwise Operations

One of the most powerful features of bitmapist is the ability to perform bitwise operations on event sets. This allows you to combine different event data to answer complex questions, such as user retention, feature adoption funnels, and segment comparisons.

Bitmapist supports four bitwise operations: AND, OR, XOR, and NOT.

Behind the scenes, these operations use the Redis BITOP command, which is extremely fast. When you perform a bit operation, bitmapist creates a temporary Redis key to store the result. These keys can be managed using the functions described in the Data Management guide.

There are two ways to perform these operations: using standard Python operators or using the explicit BitOp classes.

Using Python Operators (&, |, ^, ~)

This is the most common and intuitive way to combine event sets.

  • & (AND): Intersection - users who are in both sets.
  • | (OR): Union - users who are in either set.
  • ^ (XOR): Exclusive OR - users who are in one set but not both.
  • ~ (NOT): Inversion - users who are not in the set. (Note: ~ operates on a single event set).

Example: User Retention

Let's find out how many users who were active last month are also active this month.

from datetime import datetime, timezone
from bitmapist import MonthEvents

now = datetime.now(tz=timezone.utc)

last_month_active = MonthEvents('active').prev()
this_month_active = MonthEvents('active')

# Find users active in both months using the AND operator
retained_users = last_month_active & this_month_active

print(f"Users active last month: {len(last_month_active)}")
print(f"Users active this month: {len(this_month_active)}")
print(f"Users active in both months (retained): {len(retained_users)}")

# You can check for individual users in the resulting set
if 123 in retained_users:
    print("User 123 is a retained user!")

Using BitOp Classes

The BitOpAnd, BitOpOr, BitOpXor, and BitOpNot classes provide an explicit way to perform the same operations. This can sometimes be clearer for complex or nested operations.

Example: Nested Operations

Let's find users who were active last month AND this month, AND are also premium users.

from bitmapist import BitOpAnd, MonthEvents, UniqueEvents

last_month_active = MonthEvents('active').prev()
this_month_active = MonthEvents('active')
premium_users = UniqueEvents('premium_user')

# Create an intermediate result for retained users
retained_users = BitOpAnd(last_month_active, this_month_active)

# Combine the intermediate result with premium users
retained_premium_users = BitOpAnd(retained_users, premium_users)

print(f"Retained premium users: {len(retained_premium_users)}")

# This could also be written with operators:
# retained_premium_users = (last_month_active & this_month_active) & premium_users

Combining Different Event Types

You can freely mix and match different event types, including time-based and unique events.

Example: A/B Test Analysis

How many users who signed up via the 'new' form are active today?

from bitmapist import DayEvents, UniqueEvents

active_today = DayEvents('active')
ab_group_new = UniqueEvents('signup_form:new')

active_users_in_new_group = active_today & ab_group_new

print(f"Active users from 'new' signup form: {len(active_users_in_new_group)}")