import pandas as pd from functools import reduce from random import randint from causalml.dataset import make_uplift_classification class UpliftSimulation: def __init__(self, n=50000, y_name='conversion', treatment_group_keys=['control', 'discount_05', 'discount_10', 'discount_15'], n_classification_features=15, n_classification_informative=7, n_classification_repeated=0, n_uplift_increase_dict={'discount_05': 4, 'discount_10': 3, 'discount_15': 3}, n_uplift_decrease_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0}, delta_uplift_increase_dict={'discount_05': 0.0020, 'discount_10': 0.0045, 'discount_15': 0.008}, delta_uplift_decrease_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0}, n_uplift_increase_mix_informative_dict={'discount_05': 3, 'discount_10': 2, 'discount_15': 3}, n_uplift_decrease_mix_informative_dict={'discount_05': 0, 'discount_10': 0, 'discount_15': 0}, positive_class_proportion=0.05, random_seed=8097): self.n = n self.y_name = y_name self.treatment_group_keys = treatment_group_keys self.n_classification_features = n_classification_features self.n_classification_informative = n_classification_informative self.n_classification_repeated = n_classification_repeated self.n_uplift_increase_dict = n_uplift_increase_dict self.n_uplift_decrease_dict = n_uplift_decrease_dict self.delta_uplift_increase_dict = delta_uplift_increase_dict self.delta_uplift_decrease_dict = delta_uplift_decrease_dict self.n_uplift_increase_mix_informative_dict = n_uplift_increase_mix_informative_dict self.n_uplift_decrease_mix_informative_dict = n_uplift_decrease_mix_informative_dict self.positive_class_proportion = positive_class_proportion self.random_seed = random_seed self.df = None self.X_names = None def simulate_dataset(self): self.df, self.X_names = make_uplift_classification( treatment_name=self.treatment_group_keys, y_name=self.y_name, n_samples=self.n, n_classification_features=self.n_classification_features, n_classification_informative=self.n_classification_informative, n_classification_repeated=self.n_classification_repeated, n_uplift_increase_dict=self.n_uplift_increase_dict, n_uplift_decrease_dict=self.n_uplift_decrease_dict, delta_uplift_increase_dict=self.delta_uplift_increase_dict, delta_uplift_decrease_dict=self.delta_uplift_decrease_dict, n_uplift_increase_mix_informative_dict=self.n_uplift_increase_mix_informative_dict, n_uplift_decrease_mix_informative_dict=self.n_uplift_decrease_mix_informative_dict, positive_class_proportion=self.positive_class_proportion, random_seed=self.random_seed, ) def apply_discounts_and_clean(self): discounts_dict = {'control': 0, 'discount_05': 0.05, 'discount_10': 0.10, 'discount_15': 0.15} self.df['discount'] = self.df['treatment_group_key'] self.df = self.df.replace({"discount": discounts_dict}) self.df.drop(columns=['treatment_effect'], inplace=True) def postprocess_tables(self): # Add a synthetic UserID for each entry self.df['UserID'] = range(len(self.df)) # Mapping the columns informative_cols = [col for col in self.df.columns if 'informative' in col] uplift_cols = [col for col in self.df.columns if 'uplift' in col] irrelevant_cols = [col for col in self.df.columns if 'irrelevant' in col] transaction_cols = ['treatment_group_key', 'conversion', 'discount'] # User Demographics and Profiles Table (Including Informative Features) user_profiles = self.df[['UserID'] + informative_cols].copy() # Web Interaction Data Table (This might need adjustment based on actual data) # If any of the 'informative' columns relate to web interaction, include them here. # Uplift-Related Data Table uplift_data = self.df[['UserID'] + uplift_cols].copy() # Adjusting the Uplift-Related Data table to include the mixed features mixed_uplift_columns = ['x31_increase_mix', 'x22_increase_mix', 'x20_increase_mix', 'x33_increase_mix', 'x32_increase_mix', 'x27_increase_mix', 'x21_increase_mix', 'x26_increase_mix'] # Assuming uplift_data already includes the 'UserID' column uplift_data = pd.concat([uplift_data, self.df[mixed_uplift_columns]], axis=1) # Irrelevant Data Table irrelevant_data = self.df[['UserID'] + irrelevant_cols].copy() # Transaction Data Table transaction_data = self.df[['UserID'] + transaction_cols].copy() user_profiles.columns = [ 'UserID', 'AgeIndex', 'IncomeIndex', 'PurchaseFrequencyIndex', 'AccountLifetimeIndex', 'AverageTransactionValueIndex', 'PreferredPaymentMethodIndex', 'RegionIndex' ] uplift_data.columns = [ 'UserID', 'EmailDiscountCTRIndex', 'WebDiscountCTRIndex', 'SocialMediaEngagementIndex', 'DirectMailDiscountResponseIndex', 'InAppDiscountEngagementIndex', 'FlashSaleParticipationIndex', 'SeasonalPromoInterestIndex', 'LoyaltyProgramEngagementIndex', 'ReferralBonusUsageIndex', 'DiscountCodeRedemptionIndex', 'VIPSaleAccessIndex', 'EarlyAccessOptInIndex', 'ProductReviewAfterDiscountIndex', 'UpsellConversionIndex', 'CrossSellInterestIndex', 'BundlePurchaseIndex', 'SubscriptionUpgradeIndex', 'CustomerFeedbackIndex' ] irrelevant_data.columns = [ 'UserID', 'BrowserTypeIndex', 'DeviceCategoryIndex', 'OperatingSystemIndex', 'SessionStartTimeIndex', 'LanguagePreferenceIndex', 'NewsletterSubscriptionIndex', 'AccountVerificationStatusIndex', 'AdBlockerPresenceIndex' ] # transaction_data.columns = [ # 'UserID', 'DiscountCategoryIndex', 'PurchaseIndex', 'DiscountPercentageIndex' # ] transaction_data.columns = ['UserID'] + transaction_cols # List of all DataFrames to be merged self.dataframes = [user_profiles, uplift_data, irrelevant_data, transaction_data] # Merge all DataFrames on 'UserID' in one line self.df = reduce(lambda left, right: pd.merge(left, right, on='UserID'), self.dataframes) def add_monetary_effect(self): # Adding a monetary effect column def base_price(df, informative_features): if df.conversion == 0: base_price = 0 else: base_price = randint(1, 100) return base_price informative_features = [k for k in self.X_names if 'informative' in k] self.df['base_price'] = self.df.apply(lambda x: base_price(x, informative_features), axis=1) self.df['discounted_price'] = self.df['base_price']*(1-self.df['discount']) self.df['benefit'] = self.df['discounted_price']-0.8*self.df['base_price']