| Current Path : /home/deltalab/PMS/recommendations/user_profiling/components/ |
| Current File : //home/deltalab/PMS/recommendations/user_profiling/components/ProfileBased_RS.py |
from collections import defaultdict
from datetime import datetime
import json
from _library.visual_utils import visualize_message, visualize_recommendations
from components.collection_analyzer import ProductionAreaCollectionAnalyzer,AttributeCollectionAnalyzer
from datetime import datetime
import numpy as np
class ProfileBased_RS:
def __init__(self, platformProducts, platfromOrders):
self.platformProducts = platformProducts
self.platfromOrders = platfromOrders
def _generateRecomList(self, df, rs_name):
# Select relavant columns
selected_cols = ['Vendor', 'indaco_sku', 'SKU', 'Title', 'Product Type', 'Seller', 'inTrentino_source']
df = df[selected_cols]
# Rename only relevant columns
df = df.rename(
columns = {
'Title': 'product_name',
'Product Type': 'item_type',
'Vendor': 'item_vendor',
'SKU': 'item_sku',
'Seller': 'seller'
}
)
# Generate the list of recommendations
df['rs_source'] = rs_name
recommendations = df.to_dict(orient = 'records')
return recommendations
def brandBased_rs(self, user_profile, verbose):
visualize_message('[RS] Brand based')
# Retrieve the user attributes
user_brands = user_profile['brands']
purchased_products = user_profile['unique_products']
# Filter products
product_subset = self.platformProducts[self.platformProducts['Vendor'].isin(user_brands)]
product_subset = product_subset[~ product_subset['SKU'].isin(purchased_products)]
if(user_profile['adult'] == 0):
print("Filtering products to analyze (user not an adult)")
product_subset = product_subset[product_subset['isforadult'] == False]
# Generate the list of recommendations
brandBased_recom = self._generateRecomList(df = product_subset, rs_name = 'brandBased_recommendations')
if verbose:
print(f"User brands ({len(user_brands)}):", ' | '.join(user_brands))
print(f"Purchased products ({len(purchased_products)}):", ' | '.join(purchased_products))
print(product_subset)
return brandBased_recom
def categoryBased_rs(self, user_profile, verbose):
visualize_message('[RS] Category based')
# Retrieve the user attributes
user_categories = user_profile['categories']
purchased_products = user_profile['unique_products']
# Filter products
product_subset = self.platformProducts[self.platformProducts['Product Type'].isin(user_categories)]
product_subset = product_subset[~ product_subset['SKU'].isin(purchased_products)]
if(user_profile['adult'] == 0):
print("Filtering products to analyze (user not an adult)")
product_subset = product_subset[product_subset['isforadult'] == False]
# Generate the list of recommendations
categoryBased_recom = self._generateRecomList(df = product_subset, rs_name = 'categoryBased_recommendations')
if verbose:
print(f"User categories ({len(user_categories)}):", ' | '.join(user_categories))
print('-' * 90)
print(f"Purchased products ({len(purchased_products)}):\n-->", '\n--> '.join(purchased_products))
print('-' * 90)
print(product_subset)
return categoryBased_recom
def attributeBased_rs(self,user_profile, limit_bundleDim, verbose,capacity_dictionary_name):
visualize_message('[RS] Attribute Based')
# Analyze the collections
collectionAnalyzer = AttributeCollectionAnalyzer("products", self.platformProducts, self.platfromOrders, user_profile, limit_bundleDim,capacity_dictionary_name)
collection_score, selectedCollection_items, selectedCollection_explaination = collectionAnalyzer.analyze_collections(verbose)
# Generate recommendations
recommended_products = self.platformProducts[self.platformProducts['SKU'].isin(selectedCollection_items)].copy()
recommended_products['rank'] = recommended_products['SKU'].apply(lambda sku: selectedCollection_items.index(sku) + 1)
rs_name = "veganBased_recommendations"
if(capacity_dictionary_name == "biodynamic_food_recom"):
rs_name = "biodinamicBased_recommendations"
elif(capacity_dictionary_name == "bio_food_recom"):
rs_name = "bioBased_recommendations"
elif(capacity_dictionary_name == "glutenfree_food_recom"):
rs_name = "glutenfreeBased_recommendations"
recommended_products = self._generateRecomList(df = recommended_products, rs_name = rs_name)
return recommended_products, selectedCollection_explaination
def productionAreaBased_rs(self, user_profile, limit_bundleDim, verbose): #inerire parametro attributi
visualize_message('[RS] Production Area')
# retrieve the production areas of the user
user_prodAreas = user_profile['production_areas']
if len(user_prodAreas) == 0:
print("\t\t\t\tNo production areas linked\n")
return [], '', ''
# Analyze the products of the collections
ranked_productionAreas = dict()
for idk, production_area in enumerate(user_prodAreas):
if verbose:
print(f"\nProduction area ({idk + 1}/{len(user_prodAreas)}):", production_area)
# Retrieve the collection items
cond = self.platformProducts['production_areas'] == production_area
collection_items = self.platformProducts[cond]
# Analyze the collections
collectionAnalyzer = ProductionAreaCollectionAnalyzer(production_area, collection_items, self.platfromOrders, user_profile, limit_bundleDim,"production_area")
collection_score, selectedCollection_items, selectedCollection_explaination = collectionAnalyzer.analyze_collections(verbose)
# Save the information concerning this collection
collection_info = {
'collection_score': collection_score,
'collection_items': selectedCollection_items,
'explaination': selectedCollection_explaination}
ranked_productionAreas[production_area] = collection_info
# Sort the production areas according to its scores
ranked_productionAreas = dict(sorted(ranked_productionAreas.items(),
key = lambda dict_item: dict_item[1]['collection_score'],
reverse = True))
if verbose:
print("\nUSER COLLECTION:", json.dumps(ranked_productionAreas, indent = 4))
# Select one production area and its products
selected_productionArea = list(ranked_productionAreas.keys())[0]
selected_products = ranked_productionAreas[selected_productionArea]['collection_items']
explaination = ranked_productionAreas[selected_productionArea]['explaination']
# Generate recommendations
recommended_products = self.platformProducts[self.platformProducts['SKU'].isin(selected_products)].copy()
recommended_products['rank'] = recommended_products['SKU'].apply(lambda sku: selected_products.index(sku) + 1)
recommended_products = self._generateRecomList(df = recommended_products, rs_name = 'productionAreaBased_recommendations')
return recommended_products, selected_productionArea, explaination
def generateBundle(self, recommendationsByMethod):
visualize_message('Generate the bundle')
# Count the items
all_recommendations, all_productNames = list(), list()
for recommendations in recommendationsByMethod.values():
if isinstance(recommendations, dict):
recommendations = recommendations['recommendations']
# Save the recommendations
all_recommendations.extend(recommendations)
# Save the recommendation product names
product_names = [recom['product_name'] for recom in recommendations]
all_productNames.extend(product_names)
# Count the items
unique_products, counter = np.unique(all_productNames, return_counts = True)
unique_products = dict(zip(unique_products, counter))
# Sort the recommendations according to: (A) the item frequency (B) the rs method
rs_methods = list(reversed(recommendationsByMethod.keys()))
all_recommendations = sorted(all_recommendations,
key = lambda recom: (
unique_products[recom['product_name']],
rs_methods.index(recom['rs_source'])),
reverse = True)
# Drop duplicated
filtered_recommendations = []
for recom in all_recommendations:
already_recom = [recom['product_name'] for recom in filtered_recommendations]
if recom['product_name'] not in already_recom:
recom['rank'] = len(filtered_recommendations) + 1
filtered_recommendations.append(recom)
filtered_recommendations = sorted(filtered_recommendations, key = lambda recom: recom['rank'])
return filtered_recommendations
def userWise_recommendations(self, user_id, user_profile, collectionBased_bundleDim, verbose):
visualize_message(f'USER ID: {user_id}', width = 110)
# Initialize
explaination = ""
users_recommendations = defaultdict(dict)
start_time = datetime.now()
# ------------------------------- METHOD 1: Production Areas ---------------------------------
productionAreaBased_recom, selected_productionArea, explaination= self.productionAreaBased_rs(user_profile, collectionBased_bundleDim, verbose)
users_recommendations['productionAreaBased_recommendations']['recommendations'] = productionAreaBased_recom
if len(productionAreaBased_recom) > 0:
users_recommendations['productionAreaBased_recommendations']['production_area'] = selected_productionArea
users_recommendations['productionAreaBased_recommendations']['explaination'] = explaination
print(f"[Production area based] Recommendations:", len(productionAreaBased_recom))
# -----------------------------------------------------------------------------------
# ------------------------------- METHOD 2: Brands ---------------------------------
brandBased_recom = self.brandBased_rs(user_profile, verbose)
users_recommendations['brandBased_recommendations']['recommendations'] = brandBased_recom
if len(brandBased_recom) > 0:
# explaination = "Ti potrebbero interessare altri prodotti dei marchi precedentemente acquistati"
explaination = "I marchi che hai acquistato precedentemente propongono anche"
users_recommendations['brandBased_recommendations']['explaination'] = explaination
print(f"[Brand based] Recommendations:", len(brandBased_recom))
# -----------------------------------------------------------------------------------
# ------------------------------- METHOD 3: Categories ---------------------------------
categoryBased_recom = self.categoryBased_rs(user_profile, verbose)
users_recommendations['categoryBased_recommendations']['recommendations'] = categoryBased_recom
if len(categoryBased_recom) > 0:
explaination = "Ti potrebbero interessare altri prodotti delle categorie acquistate precedentemente"
users_recommendations['categoryBased_recommendations']['explaination'] = explaination
print(f"[Category based] Recommendations:", len(categoryBased_recom))
# -----------------------------------------------------------------------------------
# ------------------------------- METHOD 4: Attribute Based Products ---------------------------------
attributeBased_recom,explaination = [], ''
if(user_profile['bio_percentage'] > 0.2):
attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"bio_food_recom")
users_recommendations['bioBased_recommendations']['recommendations'] = attributeBased_recom
if len(attributeBased_recom) > 0:
users_recommendations['bioBased_recommendations']['explaination'] = explaination
else:
users_recommendations['bioBased_recommendations']['explaination'] = ""
if(user_profile['biodinamic_percentage'] > 0.2):
attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"biodynamic_food_recom")
users_recommendations['biodinamicBased_recommendations']['recommendations'] = attributeBased_recom
if len(attributeBased_recom) > 0:
users_recommendations['biodinamicBased_recommendations']['explaination'] = explaination
else:
users_recommendations['biodinamicBased_recommendations']['explaination'] = ""
if(user_profile['vegan_percentage'] > 0.2):
attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"vegan_food_recom")
users_recommendations['veganBased_recommendations']['recommendations'] = attributeBased_recom
if len(attributeBased_recom) > 0:
users_recommendations['veganBased_recommendations']['explaination'] = explaination
else:
users_recommendations['veganBased_recommendations']['explaination'] = ""
if(user_profile['gluten_free_percentage'] > 0.2):
attributeBased_recom, explaination= self.attributeBased_rs(user_profile, 2, verbose,"glutenfree_food_recom")
users_recommendations['glutenfreeBased_recommendations']['recommendations'] = attributeBased_recom
if len(attributeBased_recom) > 0:
users_recommendations['glutenfreeBased_recommendations']['explaination'] = explaination
else:
users_recommendations['glutenfreeBased_recommendations']['explaination'] = ""
# ------------------------------- Bundle with unique products ---------------------------------
user_bundle = self.generateBundle(users_recommendations)
users_recommendations['recommendation_bundle']['recommendations'] = user_bundle
if len(user_bundle) > 0:
explaination = "Ti potrebbero interessare"
users_recommendations['recommendation_bundle']['explaination'] = explaination
print(f"[Bundle] Recommendations:", len(user_bundle))
# -----------------------------------------------------------------------------------
# Computation time
raw_duration = datetime.now() - start_time
temporal_unit = lambda total_seconds: 'ms' if total_seconds < 1 else 's' if total_seconds < 60 else 'm'
user_computationalTime = np.timedelta64(raw_duration, temporal_unit(raw_duration.total_seconds()))
print('\n\nUser duration:', user_computationalTime, "\n\n")
if verbose:
visualize_recommendations(user_bundle)
# Save the computed recommendations
return users_recommendations, user_computationalTime
def userWise_recommendations_multiProcess(self, queue, user_id, user_profile, verbose):
recommendations, computationalTime = self.userWise_recommendations(
user_id, user_profile, verbose)
processOutput = ({user_id: recommendations},
{user_id: computationalTime})
queue.put(processOutput)