How to write Python dict comprehension for varying key lengths

Question:

i am building a simple weightage model with some relationship between the components. I have a skeleton code that does the relationship building between 2 keys, but when increasing to 3/4 keys, i cant find a way to implement it to cater to varying lengths of multiplier key combinations.

Thanks, any help is appreciated!

component_weights = {
    'quality': 0.3,
    'price': 0.2,
    'customer_service': 0.2,
    'features': 0.3,
}

# Define relationship weight multipliers
relationship_multipliers = {
    ('quality', 'customer_service','features'): 1.6,
    ('quality', 'customer_service'): 1.5,
    ('customer_service', 'features'): 1.5,
}

# Evaluate each component and relationship
component_values = {
    'quality': True,
    'price': False,
    'customer_service': True,
    'features': True,
}

component_scores = {
    name: value * component_weights[name]
    for name, value in component_values.items()
}
print(component_scores)

# How can i change this line to take in varying sizes of relationship_multipliers combinations?

relationship_scores = {
    relationship: (
       # This only works if there is 2 keys in the multipliers dict
        component_scores[relationship[0]] *
        component_scores[relationship[1]] *
        relationship_multipliers[relationship]
    )
    for relationship in relationship_multipliers.keys()
    if component_values[relationship[0]] and component_values[relationship[1]]

}
print(relationship_scores)
weighted_score = sum(component_scores.values()) + sum(relationship_scores.values())

print(weighted_score)

Asked By: Jerru

||

Answers:

If you want to handle varying key lengths, you can do nested loops to iterate over all possible combination of key in relationship_multipliers and extract the good multipliers, try this :

def calculate_relationship_score(combination, multiplier_cache):
    # Base case: if combination is a single key, return the component score
    if len(combination) == 1:
        return component_scores[combination[0]]
    
    # Check if we have already calculated the relationship score for this combination
    if combination in multiplier_cache:
        return multiplier_cache[combination]
    
    # Calculate relationship score by recursively calculating scores for smaller combinations
    relationship_score = 0
    for i in range(1, len(combination)):
        for sub_combination in itertools.combinations(combination, i):
            sub_combination_score = calculate_relationship_score(sub_combination, multiplier_cache)
            other_keys = set(combination) - set(sub_combination)
            other_score = calculate_relationship_score(tuple(sorted(other_keys)), multiplier_cache)
            sub_multiplier = relationship_multipliers.get(frozenset(sub_combination), 1)
            relationship_score += sub_combination_score * other_score * sub_multiplier
    
    # Cache the relationship score for future use
    multiplier_cache[combination] = relationship_score
    return relationship_score

# Evaluate each component and relationship
component_values = {
    'quality': True,
    'price': False,
    'customer_service': True,
    'features': True,
}

component_scores = {
    name: value * component_weights[name]
    for name, value in component_values.items()
}
print(component_scores)

# Calculate relationship scores for each combination of keys
relationship_scores = {}
multiplier_cache = {}
for i in range(1, len(component_values) + 1):
    for combination in itertools.combinations(component_values.keys(), i):
        if all(component_values[key] for key in combination):
            relationship_scores[combination] = calculate_relationship_score(combination, multiplier_cache)

print(relationship_scores)
weighted_score = sum(component_scores.values()) + sum(relationship_scores.values())
print(weighted_score)

Answered By: Baki

I’ve found a better way to solve my problem, if anyone needs my solution:

relationship_multipliers = {
    (('quality', 'customer_service','features')), 1.6,
    (('quality', 'customer_service')), 1.5,
    (('customer_service', 'features')), 1.5,
}

relationship_scores = {}
for relationship, weight in relationship_multipliers:
 if all(component_values.get(component,False) for component in relationship):
  scores = [component_scores[component] for component in relationship]
  relationship_scores[relationship] = sum(scores) * weight

total_score = sum(component_scores.values())
if relationship_scores:
 total_score = max(relationship_scores.values())

return total_score

hope this helps.

Answered By: Jerru
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.