PawMatchAI / scoring_calculation_system.py
DawnC's picture
Update scoring_calculation_system.py
3e0fc3d verified
raw
history blame
86 kB
from dataclasses import dataclass
from breed_health_info import breed_health_info
from breed_noise_info import breed_noise_info
import traceback
import math
import random
# @dataclass
# class UserPreferences:
# """使用者偏好設定的資料結構"""
# living_space: str # "apartment", "house_small", "house_large"
# yard_access: str # "no_yard", "shared_yard", "private_yard"
# exercise_time: int # minutes per day
# exercise_type: str # "light_walks", "moderate_activity", "active_training"
# grooming_commitment: str # "low", "medium", "high"
# experience_level: str # "beginner", "intermediate", "advanced"
# time_availability: str # "limited", "moderate", "flexible"
# has_children: bool
# children_age: str # "toddler", "school_age", "teenager"
# noise_tolerance: str # "low", "medium", "high"
# space_for_play: bool
# other_pets: bool
# climate: str # "cold", "moderate", "hot"
# health_sensitivity: str = "medium"
# barking_acceptance: str = None
# def __post_init__(self):
# """在初始化後運行,用於設置派生值"""
# if self.barking_acceptance is None:
# self.barking_acceptance = self.noise_tolerance
@dataclass
class UserPreferences:
"""使用者偏好設定的資料結構,整合基本條件與進階評估參數"""
living_space: str # "apartment", "house_small", "house_large"
yard_access: str # "no_yard", "shared_yard", "private_yard"
exercise_time: int # 每日運動時間(分鐘)
exercise_type: str # "light_walks", "moderate_activity", "active_training"
grooming_commitment: str # "low", "medium", "high"
experience_level: str # "beginner", "intermediate", "advanced"
time_availability: str # "limited", "moderate", "flexible"
has_children: bool
children_age: str # "toddler", "school_age", "teenager"
noise_tolerance: str # "low", "medium", "high"
space_for_play: bool
other_pets: bool
climate: str # "cold", "moderate", "hot"
living_floor: int = 1 # 居住樓層,對公寓住戶特別重要
exercise_intensity: str = "moderate" # "low", "moderate", "high"
home_alone_time: int = 4 # 每日獨處時間(小時)
health_sensitivity: str = "medium" # "low", "medium", "high"
barking_acceptance: str = None # 如果未指定,默認使用 noise_tolerance
lifestyle_activity: str = "moderate" # "sedentary", "moderate", "active"
def __post_init__(self):
"""初始化後執行,用於設置派生值和驗證"""
if self.barking_acceptance is None:
self.barking_acceptance = self.noise_tolerance
@staticmethod
def calculate_breed_bonus(breed_info: dict, user_prefs: 'UserPreferences') -> float:
"""計算品種額外加分"""
bonus = 0.0
temperament = breed_info.get('Temperament', '').lower()
# 1. 壽命加分(最高0.05)
try:
lifespan = breed_info.get('Lifespan', '10-12 years')
years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
longevity_bonus = min(0.05, (max(years) - 10) * 0.01)
bonus += longevity_bonus
except:
pass
# 2. 性格特徵加分(最高0.15)
positive_traits = {
'friendly': 0.05,
'gentle': 0.05,
'patient': 0.05,
'intelligent': 0.04,
'adaptable': 0.04,
'affectionate': 0.04,
'easy-going': 0.03,
'calm': 0.03
}
negative_traits = {
'aggressive': -0.08,
'stubborn': -0.06,
'dominant': -0.06,
'aloof': -0.04,
'nervous': -0.05,
'protective': -0.04
}
personality_score = sum(value for trait, value in positive_traits.items() if trait in temperament)
personality_score += sum(value for trait, value in negative_traits.items() if trait in temperament)
bonus += max(-0.15, min(0.15, personality_score))
# 3. 適應性加分(最高0.1)
adaptability_bonus = 0.0
if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment":
adaptability_bonus += 0.05
if 'adaptable' in temperament or 'versatile' in temperament:
adaptability_bonus += 0.05
bonus += min(0.1, adaptability_bonus)
# 4. 家庭相容性(最高0.1)
if user_prefs.has_children:
family_traits = {
'good with children': 0.06,
'patient': 0.05,
'gentle': 0.05,
'tolerant': 0.04,
'playful': 0.03
}
unfriendly_traits = {
'aggressive': -0.08,
'nervous': -0.07,
'protective': -0.06,
'territorial': -0.05
}
# 年齡評估這樣能更細緻
age_adjustments = {
'toddler': {'bonus_mult': 0.7, 'penalty_mult': 1.3},
'school_age': {'bonus_mult': 1.0, 'penalty_mult': 1.0},
'teenager': {'bonus_mult': 1.2, 'penalty_mult': 0.8}
}
adj = age_adjustments.get(user_prefs.children_age,
{'bonus_mult': 1.0, 'penalty_mult': 1.0})
family_bonus = sum(value for trait, value in family_traits.items()
if trait in temperament) * adj['bonus_mult']
family_penalty = sum(value for trait, value in unfriendly_traits.items()
if trait in temperament) * adj['penalty_mult']
bonus += min(0.15, max(-0.2, family_bonus + family_penalty))
# 5. 專門技能加分(最高0.1)
skill_bonus = 0.0
special_abilities = {
'working': 0.03,
'herding': 0.03,
'hunting': 0.03,
'tracking': 0.03,
'agility': 0.02
}
for ability, value in special_abilities.items():
if ability in temperament.lower():
skill_bonus += value
bonus += min(0.1, skill_bonus)
# 6. 適應性評估 - 根據具體環境給予更細緻的評分
adaptability_bonus = 0.0
if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment":
adaptability_bonus += 0.08 # 小型犬更適合公寓
# 環境適應性評估
if 'adaptable' in temperament or 'versatile' in temperament:
if user_prefs.living_space == "apartment":
adaptability_bonus += 0.10 # 適應性在公寓環境更重要
else:
adaptability_bonus += 0.05 # 其他環境仍有加分
# 氣候適應性
description = breed_info.get('Description', '').lower()
climate = user_prefs.climate
if climate == 'hot':
if 'heat tolerant' in description or 'warm climate' in description:
adaptability_bonus += 0.08
elif 'thick coat' in description or 'cold climate' in description:
adaptability_bonus -= 0.10
elif climate == 'cold':
if 'thick coat' in description or 'cold climate' in description:
adaptability_bonus += 0.08
elif 'heat tolerant' in description or 'short coat' in description:
adaptability_bonus -= 0.10
bonus += min(0.15, adaptability_bonus)
return min(0.5, max(-0.25, bonus))
@staticmethod
def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict:
"""
計算額外的評估因素,結合品種特性與使用者需求的全面評估系統
此函數整合了:
1. 多功能性評估 - 品種的多樣化能力
2. 訓練性評估 - 學習和服從能力
3. 能量水平評估 - 活力和運動需求
4. 美容需求評估 - 護理和維護需求
5. 社交需求評估 - 與人互動的需求程度
6. 氣候適應性 - 對環境的適應能力
7. 運動類型匹配 - 與使用者運動習慣的契合度
8. 生活方式適配 - 與使用者日常生活的匹配度
"""
factors = {
'versatility': 0.0, # 多功能性
'trainability': 0.0, # 可訓練度
'energy_level': 0.0, # 能量水平
'grooming_needs': 0.0, # 美容需求
'social_needs': 0.0, # 社交需求
'weather_adaptability': 0.0,# 氣候適應性
'exercise_match': 0.0, # 運動匹配度
'lifestyle_fit': 0.0 # 生活方式適配度
}
temperament = breed_info.get('Temperament', '').lower()
description = breed_info.get('Description', '').lower()
size = breed_info.get('Size', 'Medium')
# 1. 多功能性評估 - 加強品種用途評估
versatile_traits = {
'intelligent': 0.25,
'adaptable': 0.25,
'trainable': 0.20,
'athletic': 0.15,
'versatile': 0.15
}
working_roles = {
'working': 0.20,
'herding': 0.15,
'hunting': 0.15,
'sporting': 0.15,
'companion': 0.10
}
# 計算特質分數
trait_score = sum(value for trait, value in versatile_traits.items()
if trait in temperament)
# 計算角色分數
role_score = sum(value for role, value in working_roles.items()
if role in description)
# 根據使用者需求調整多功能性評分
purpose_traits = {
'light_walks': ['calm', 'gentle', 'easy-going'],
'moderate_activity': ['adaptable', 'balanced', 'versatile'],
'active_training': ['intelligent', 'trainable', 'working']
}
if user_prefs.exercise_type in purpose_traits:
matching_traits = sum(1 for trait in purpose_traits[user_prefs.exercise_type]
if trait in temperament)
trait_score += matching_traits * 0.15
factors['versatility'] = min(1.0, trait_score + role_score)
# 2. 訓練性評估 - 考慮使用者經驗
trainable_traits = {
'intelligent': 0.3,
'eager to please': 0.3,
'trainable': 0.2,
'quick learner': 0.2,
'obedient': 0.2
}
base_trainability = sum(value for trait, value in trainable_traits.items()
if trait in temperament)
# 根據使用者經驗調整訓練性評分
experience_multipliers = {
'beginner': 1.2, # 新手更需要容易訓練的狗
'intermediate': 1.0,
'advanced': 0.8 # 專家能處理較難訓練的狗
}
factors['trainability'] = min(1.0, base_trainability *
experience_multipliers.get(user_prefs.experience_level, 1.0))
# 3. 能量水平評估 - 強化運動需求匹配
exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper()
energy_levels = {
'VERY HIGH': {
'score': 1.0,
'min_exercise': 120,
'ideal_exercise': 150
},
'HIGH': {
'score': 0.8,
'min_exercise': 90,
'ideal_exercise': 120
},
'MODERATE': {
'score': 0.6,
'min_exercise': 60,
'ideal_exercise': 90
},
'LOW': {
'score': 0.4,
'min_exercise': 30,
'ideal_exercise': 60
}
}
breed_energy = energy_levels.get(exercise_needs, energy_levels['MODERATE'])
# 計算運動時間匹配度
if user_prefs.exercise_time >= breed_energy['ideal_exercise']:
energy_score = breed_energy['score']
else:
# 如果運動時間不足,按比例降低分數
deficit_ratio = max(0.4, user_prefs.exercise_time / breed_energy['ideal_exercise'])
energy_score = breed_energy['score'] * deficit_ratio
factors['energy_level'] = energy_score
# 4. 美容需求評估 - 加入更多毛髮類型考量
grooming_needs = breed_info.get('Grooming Needs', 'MODERATE').upper()
grooming_levels = {
'HIGH': 1.0,
'MODERATE': 0.6,
'LOW': 0.3
}
# 特殊毛髮類型評估
coat_adjustments = 0
if 'long coat' in description:
coat_adjustments += 0.2
if 'double coat' in description:
coat_adjustments += 0.15
if 'curly' in description:
coat_adjustments += 0.15
# 根據使用者承諾度調整
commitment_multipliers = {
'low': 1.5, # 低承諾度時加重美容需求的影響
'medium': 1.0,
'high': 0.8 # 高承諾度時降低美容需求的影響
}
base_grooming = grooming_levels.get(grooming_needs, 0.6) + coat_adjustments
factors['grooming_needs'] = min(1.0, base_grooming *
commitment_multipliers.get(user_prefs.grooming_commitment, 1.0))
# 5. 社交需求評估 - 加強家庭情況考量
social_traits = {
'friendly': 0.25,
'social': 0.25,
'affectionate': 0.20,
'people-oriented': 0.20
}
antisocial_traits = {
'independent': -0.20,
'aloof': -0.20,
'reserved': -0.15
}
social_score = sum(value for trait, value in social_traits.items()
if trait in temperament)
antisocial_score = sum(value for trait, value in antisocial_traits.items()
if trait in temperament)
# 家庭情況調整
if user_prefs.has_children:
child_friendly_bonus = 0.2 if 'good with children' in temperament else 0
social_score += child_friendly_bonus
factors['social_needs'] = min(1.0, max(0.0, social_score + antisocial_score))
# 6. 氣候適應性評估 - 更細緻的環境適應評估
climate_traits = {
'cold': {
'positive': ['thick coat', 'winter', 'cold climate'],
'negative': ['short coat', 'heat sensitive']
},
'hot': {
'positive': ['short coat', 'heat tolerant', 'warm climate'],
'negative': ['thick coat', 'cold climate']
},
'moderate': {
'positive': ['adaptable', 'all climate'],
'negative': []
}
}
climate_score = 0.4 # 基礎分數
if user_prefs.climate in climate_traits:
# 正面特質加分
climate_score += sum(0.2 for term in climate_traits[user_prefs.climate]['positive']
if term in description)
# 負面特質減分
climate_score -= sum(0.2 for term in climate_traits[user_prefs.climate]['negative']
if term in description)
factors['weather_adaptability'] = min(1.0, max(0.0, climate_score))
# 7. 運動類型匹配評估
exercise_type_traits = {
'light_walks': ['calm', 'gentle'],
'moderate_activity': ['adaptable', 'balanced'],
'active_training': ['athletic', 'energetic']
}
if user_prefs.exercise_type in exercise_type_traits:
match_score = sum(0.25 for trait in exercise_type_traits[user_prefs.exercise_type]
if trait in temperament)
factors['exercise_match'] = min(1.0, match_score + 0.5) # 基礎分0.5
# 8. 生活方式適配評估
lifestyle_score = 0.5 # 基礎分數
# 空間適配
if user_prefs.living_space == 'apartment':
if size == 'Small':
lifestyle_score += 0.2
elif size == 'Large':
lifestyle_score -= 0.2
elif user_prefs.living_space == 'house_large':
if size in ['Large', 'Giant']:
lifestyle_score += 0.2
# 時間可用性適配
time_availability_bonus = {
'limited': -0.1,
'moderate': 0,
'flexible': 0.1
}
lifestyle_score += time_availability_bonus.get(user_prefs.time_availability, 0)
factors['lifestyle_fit'] = min(1.0, max(0.0, lifestyle_score))
return factors
def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences) -> dict:
"""計算品種與使用者條件的相容性分數的優化版本"""
try:
print(f"Processing breed: {breed_info.get('Breed', 'Unknown')}")
print(f"Breed info keys: {breed_info.keys()}")
if 'Size' not in breed_info:
print("Missing Size information")
raise KeyError("Size information missing")
# def calculate_space_score(size: str, living_space: str, has_yard: bool, exercise_needs: str) -> float:
# """
# 主要改進:
# 1. 更均衡的基礎分數分配
# 2. 更細緻的空間需求評估
# 3. 強化運動需求與空間的關聯性
# """
# # 重新設計基礎分數矩陣,降低普遍分數以增加區別度
# base_scores = {
# "Small": {
# "apartment": 0.85, # 降低滿分機會
# "house_small": 0.80, # 小型犬不應在大空間得到太高分數
# "house_large": 0.75 # 避免小型犬總是得到最高分
# },
# "Medium": {
# "apartment": 0.45, # 維持對公寓環境的限制
# "house_small": 0.75, # 適中的分數
# "house_large": 0.85 # 給予合理的獎勵
# },
# "Large": {
# "apartment": 0.15, # 加重對大型犬在公寓的限制
# "house_small": 0.65, # 中等適合度
# "house_large": 0.90 # 最適合的環境
# },
# "Giant": {
# "apartment": 0.10, # 更嚴格的限制
# "house_small": 0.45, # 顯著的空間限制
# "house_large": 0.95 # 最理想的配對
# }
# }
# # 取得基礎分數
# base_score = base_scores.get(size, base_scores["Medium"])[living_space]
# # 運動需求相關的調整更加動態
# exercise_adjustments = {
# "Very High": {
# "apartment": -0.25, # 加重在受限空間的懲罰
# "house_small": -0.15,
# "house_large": -0.05
# },
# "High": {
# "apartment": -0.20,
# "house_small": -0.10,
# "house_large": 0
# },
# "Moderate": {
# "apartment": -0.10,
# "house_small": -0.05,
# "house_large": 0
# },
# "Low": {
# "apartment": 0.05, # 低運動需求在小空間反而有優勢
# "house_small": 0,
# "house_large": -0.05 # 輕微降低評分,因為空間可能過大
# }
# }
# # 根據空間類型獲取運動需求調整
# adjustment = exercise_adjustments.get(exercise_needs,
# exercise_adjustments["Moderate"])[living_space]
# # 院子效益根據品種大小和運動需求動態調整
# if has_yard:
# yard_bonus = {
# "Giant": 0.20,
# "Large": 0.15,
# "Medium": 0.10,
# "Small": 0.05
# }.get(size, 0.10)
# # 運動需求會影響院子的重要性
# if exercise_needs in ["Very High", "High"]:
# yard_bonus *= 1.2
# elif exercise_needs == "Low":
# yard_bonus *= 0.8
# current_score = base_score + adjustment + yard_bonus
# else:
# current_score = base_score + adjustment
# # 確保分數在合理範圍內,但避免極端值
# return min(0.95, max(0.15, current_score))
# def calculate_exercise_score(breed_needs: str, exercise_time: int, exercise_type: str) -> float:
# """
# 精確評估品種運動需求與使用者運動條件的匹配度
# Parameters:
# breed_needs: 品種的運動需求等級
# exercise_time: 使用者能提供的運動時間(分鐘)
# exercise_type: 使用者偏好的運動類型
# Returns:
# float: -0.2 到 0.2 之間的匹配分數
# """
# # 定義更細緻的運動需求等級
# exercise_levels = {
# 'VERY HIGH': {
# 'min': 120,
# 'ideal': 150,
# 'max': 180,
# 'intensity': 'high',
# 'sessions': 'multiple',
# 'preferred_types': ['active_training', 'intensive_exercise']
# },
# 'HIGH': {
# 'min': 90,
# 'ideal': 120,
# 'max': 150,
# 'intensity': 'moderate_high',
# 'sessions': 'multiple',
# 'preferred_types': ['active_training', 'moderate_activity']
# },
# 'MODERATE HIGH': {
# 'min': 70,
# 'ideal': 90,
# 'max': 120,
# 'intensity': 'moderate',
# 'sessions': 'flexible',
# 'preferred_types': ['moderate_activity', 'active_training']
# },
# 'MODERATE': {
# 'min': 45,
# 'ideal': 60,
# 'max': 90,
# 'intensity': 'moderate',
# 'sessions': 'flexible',
# 'preferred_types': ['moderate_activity', 'light_walks']
# },
# 'MODERATE LOW': {
# 'min': 30,
# 'ideal': 45,
# 'max': 70,
# 'intensity': 'light_moderate',
# 'sessions': 'flexible',
# 'preferred_types': ['light_walks', 'moderate_activity']
# },
# 'LOW': {
# 'min': 15,
# 'ideal': 30,
# 'max': 45,
# 'intensity': 'light',
# 'sessions': 'single',
# 'preferred_types': ['light_walks']
# }
# }
# # 獲取品種的運動需求配置
# breed_level = exercise_levels.get(breed_needs.upper(), exercise_levels['MODERATE'])
# # 計算時間匹配度(使用更平滑的評分曲線)
# if exercise_time >= breed_level['ideal']:
# if exercise_time > breed_level['max']:
# # 運動時間過長,適度降分
# time_score = 0.15 - (0.05 * (exercise_time - breed_level['max']) / 30)
# else:
# time_score = 0.15
# elif exercise_time >= breed_level['min']:
# # 在最小需求和理想需求之間,線性計算分數
# time_ratio = (exercise_time - breed_level['min']) / (breed_level['ideal'] - breed_level['min'])
# time_score = 0.05 + (time_ratio * 0.10)
# else:
# # 運動時間不足,根據差距程度扣分
# time_ratio = max(0, exercise_time / breed_level['min'])
# time_score = -0.15 * (1 - time_ratio)
# # 運動類型匹配度評估
# type_score = 0.0
# if exercise_type in breed_level['preferred_types']:
# type_score = 0.05
# if exercise_type == breed_level['preferred_types'][0]:
# type_score = 0.08 # 最佳匹配類型給予更高分數
# return max(-0.2, min(0.2, time_score + type_score))
def calculate_space_score(breed_info: dict, user_prefs: UserPreferences) -> float:
"""
計算品種與居住空間的匹配程度
這個函數實現了一個全面的空間評分系統,考慮:
1. 基本空間需求(住所類型與品種大小的匹配)
2. 樓層因素(特別是公寓住戶)
3. 戶外活動空間(院子類型及可用性)
4. 室內活動空間的實際可用性
5. 品種的特殊空間需求
Parameters:
-----------
breed_info: 包含品種特徵的字典,包括體型、活動需求等
user_prefs: 使用者偏好設定,包含居住條件相關信息
Returns:
--------
float: 0.0-1.0 之間的匹配分數
"""
# 取得品種基本信息
size = breed_info.get('Size', 'Medium')
temperament = breed_info.get('Temperament', '').lower()
exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper()
# 基礎空間需求評分矩陣 - 考慮品種大小與居住空間的基本匹配度
base_space_scores = {
"Small": {
"apartment": 0.95, # 小型犬最適合公寓
"house_small": 0.90, # 小房子也很適合
"house_large": 0.85 # 大房子可能過大
},
"Medium": {
"apartment": 0.60, # 中型犬在公寓有一定限制
"house_small": 0.85, # 小房子較適合
"house_large": 0.95 # 大房子最理想
},
"Large": {
"apartment": 0.30, # 大型犬不適合公寓
"house_small": 0.70, # 小房子稍嫌擁擠
"house_large": 1.0 # 大房子最理想
},
"Giant": {
"apartment": 0.20, # 極大型犬極不適合公寓
"house_small": 0.50, # 小房子明顯不足
"house_large": 1.0 # 大房子必需
}
}
# 取得基礎空間分數
base_score = base_space_scores.get(size, base_space_scores["Medium"])[user_prefs.living_space]
# 公寓特殊考量
if user_prefs.living_space == "apartment":
# 樓層調整
floor_penalty = 0
if user_prefs.living_floor > 1:
if size in ["Large", "Giant"]:
floor_penalty = min(0.3, (user_prefs.living_floor - 1) * 0.05)
elif size == "Medium":
floor_penalty = min(0.2, (user_prefs.living_floor - 1) * 0.03)
else:
floor_penalty = min(0.1, (user_prefs.living_floor - 1) * 0.02)
base_score = max(0.2, base_score - floor_penalty)
# 戶外空間評估
yard_scores = {
"no_yard": 0,
"shared_yard": 0.1,
"private_yard": 0.2
}
# 根據品種大小調整院子加分
yard_size_multipliers = {
"Giant": 1.2,
"Large": 1.1,
"Medium": 1.0,
"Small": 0.8
}
yard_bonus = yard_scores[user_prefs.yard_access] * yard_size_multipliers.get(size, 1.0)
# 活動空間需求評估
activity_space_score = 0
if user_prefs.space_for_play:
if exercise_needs in ["VERY HIGH", "HIGH"]:
activity_space_score = 0.15
elif exercise_needs == "MODERATE":
activity_space_score = 0.10
else:
activity_space_score = 0.05
# 品種特性評估
temperament_adjustments = 0
if 'active' in temperament or 'energetic' in temperament:
if user_prefs.living_space == 'apartment':
temperament_adjustments -= 0.15
elif user_prefs.living_space == 'house_small':
temperament_adjustments -= 0.05
if 'calm' in temperament or 'lazy' in temperament:
if user_prefs.living_space == 'apartment':
temperament_adjustments += 0.10
if 'adaptable' in temperament:
temperament_adjustments += 0.05
# 家庭環境考量
if user_prefs.has_children:
if user_prefs.living_space == 'apartment':
# 公寓中有孩童需要更多活動空間
if size in ["Large", "Giant"]:
base_score *= 0.85
elif size == "Medium":
base_score *= 0.90
# 整合所有評分因素
final_score = base_score + yard_bonus + activity_space_score + temperament_adjustments
# 確保最終分數在合理範圍內
return max(0.15, min(1.0, final_score))
def calculate_exercise_score(breed_needs: str, exercise_time: int, user_prefs: 'UserPreferences') -> float:
"""
計算品種運動需求與使用者條件的匹配分數
這個函數實現了一個精細的運動評分系統,考慮:
1. 運動時間的匹配度(0-180分鐘)
2. 運動強度的適配性
3. 品種特性對運動的特殊需求
4. 生活方式的整體活躍度
Parameters:
-----------
breed_needs: 品種的運動需求等級
exercise_time: 使用者能提供的運動時間(分鐘)
user_prefs: 使用者偏好設定,包含運動類型和強度等信息
Returns:
--------
float: 0.0-1.0 之間的匹配分數
"""
# 定義更精確的運動需求標準
exercise_levels = {
'VERY HIGH': {
'min': 120,
'ideal': 150,
'max': 180,
'intensity_required': 'high',
'intensity_factors': {'high': 1.2, 'moderate': 0.8, 'low': 0.6},
'type_bonus': {'active_training': 0.15, 'moderate_activity': 0.05, 'light_walks': -0.1}
},
'HIGH': {
'min': 90,
'ideal': 120,
'max': 150,
'intensity_required': 'moderate',
'intensity_factors': {'high': 1.1, 'moderate': 1.0, 'low': 0.7},
'type_bonus': {'active_training': 0.1, 'moderate_activity': 0.1, 'light_walks': -0.05}
},
'MODERATE': {
'min': 60,
'ideal': 90,
'max': 120,
'intensity_required': 'moderate',
'intensity_factors': {'high': 1.0, 'moderate': 1.0, 'low': 0.8},
'type_bonus': {'active_training': 0.05, 'moderate_activity': 0.1, 'light_walks': 0.05}
},
'LOW': {
'min': 30,
'ideal': 60,
'max': 90,
'intensity_required': 'low',
'intensity_factors': {'high': 0.7, 'moderate': 0.9, 'low': 1.0},
'type_bonus': {'active_training': -0.05, 'moderate_activity': 0.05, 'light_walks': 0.1}
}
}
# 獲取品種運動需求配置
breed_level = exercise_levels.get(breed_needs.upper(), exercise_levels['MODERATE'])
# 計算基礎運動時間分數
def calculate_time_score(time: int, level: dict) -> float:
if time < level['min']:
# 運動時間不足,指數下降
return max(0.3, (time / level['min']) ** 1.5)
elif time < level['ideal']:
# 運動時間接近理想,線性增長
return 0.7 + 0.3 * ((time - level['min']) / (level['ideal'] - level['min']))
elif time <= level['max']:
# 理想運動時間範圍,高分保持
return 1.0
else:
# 運動時間過多,緩慢扣分
excess = (time - level['max']) / 30 # 每超過30分鐘扣分
return max(0.7, 1.0 - (excess * 0.1))
# 計算運動時間基礎分數
time_score = calculate_time_score(exercise_time, breed_level)
# 計算運動強度匹配度
intensity_factor = breed_level['intensity_factors'].get(user_prefs.exercise_intensity, 1.0)
# 運動類型加成
type_bonus = breed_level['type_bonus'].get(user_prefs.exercise_type, 0)
# 生活方式調整
lifestyle_adjustments = {
'sedentary': -0.1,
'moderate': 0,
'active': 0.1
}
lifestyle_factor = lifestyle_adjustments.get(user_prefs.lifestyle_activity, 0)
# 整合所有因素
final_score = time_score * intensity_factor + type_bonus + lifestyle_factor
# 確保分數在合理範圍內
return max(0.1, min(1.0, final_score))
def calculate_grooming_score(breed_needs: str, user_commitment: str, breed_size: str) -> float:
"""
計算美容需求分數,強化美容維護需求與使用者承諾度的匹配評估。
這個函數特別注意品種大小對美容工作的影響,以及不同程度的美容需求對時間投入的要求。
"""
# 重新設計基礎分數矩陣,讓美容需求的差異更加明顯
base_scores = {
"High": {
"low": 0.20, # 高需求對低承諾極不合適,顯著降低初始分數
"medium": 0.65, # 中等承諾仍有挑戰
"high": 1.0 # 高承諾最適合
},
"Moderate": {
"low": 0.45, # 中等需求對低承諾有困難
"medium": 0.85, # 較好的匹配
"high": 0.95 # 高承諾會有餘力
},
"Low": {
"low": 0.90, # 低需求對低承諾很合適
"medium": 0.85, # 略微降低以反映可能過度投入
"high": 0.80 # 可能造成資源浪費
}
}
# 取得基礎分數
base_score = base_scores.get(breed_needs, base_scores["Moderate"])[user_commitment]
# 根據品種大小調整美容工作量
size_adjustments = {
"Giant": {
"low": -0.35, # 大型犬的美容工作量顯著增加
"medium": -0.20,
"high": -0.10
},
"Large": {
"low": -0.25,
"medium": -0.15,
"high": -0.05
},
"Medium": {
"low": -0.15,
"medium": -0.10,
"high": 0
},
"Small": {
"low": -0.10,
"medium": -0.05,
"high": 0
}
}
# 應用體型調整
size_adjustment = size_adjustments.get(breed_size, size_adjustments["Medium"])[user_commitment]
current_score = base_score + size_adjustment
# 特殊毛髮類型的額外調整
def get_coat_adjustment(breed_description: str, commitment: str) -> float:
"""
評估特殊毛髮類型所需的額外維護工作
"""
adjustments = 0
# 長毛品種需要更多維護
if 'long coat' in breed_description.lower():
coat_penalties = {
'low': -0.20,
'medium': -0.15,
'high': -0.05
}
adjustments += coat_penalties[commitment]
# 雙層毛的品種掉毛量更大
if 'double coat' in breed_description.lower():
double_coat_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
adjustments += double_coat_penalties[commitment]
# 捲毛品種需要定期專業修剪
if 'curly' in breed_description.lower():
curly_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
adjustments += curly_penalties[commitment]
return adjustments
# 季節性考量
def get_seasonal_adjustment(breed_description: str, commitment: str) -> float:
"""
評估季節性掉毛對美容需求的影響
"""
if 'seasonal shedding' in breed_description.lower():
seasonal_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
return seasonal_penalties[commitment]
return 0
# 專業美容需求評估
def get_professional_grooming_adjustment(breed_description: str, commitment: str) -> float:
"""
評估需要專業美容服務的影響
"""
if 'professional grooming' in breed_description.lower():
grooming_penalties = {
'low': -0.20,
'medium': -0.15,
'high': -0.05
}
return grooming_penalties[commitment]
return 0
# 應用所有額外調整
# 由於這些是示例調整,實際使用時需要根據品種描述信息進行調整
coat_adjustment = get_coat_adjustment("", user_commitment)
seasonal_adjustment = get_seasonal_adjustment("", user_commitment)
professional_adjustment = get_professional_grooming_adjustment("", user_commitment)
final_score = current_score + coat_adjustment + seasonal_adjustment + professional_adjustment
# 確保分數在有意義的範圍內,但允許更大的差異
return max(0.1, min(1.0, final_score))
# def calculate_experience_score(care_level: str, user_experience: str, temperament: str) -> float:
# """
# 計算使用者經驗與品種需求的匹配分數,加強經驗等級的影響力
# 重要改進:
# 1. 擴大基礎分數差異
# 2. 加重困難特徵的懲罰
# 3. 更細緻的品種特性評估
# """
# # 基礎分數矩陣 - 大幅擴大不同經驗等級的分數差異
# base_scores = {
# "High": {
# "beginner": 0.10, # 降低起始分,高難度品種對新手幾乎不推薦
# "intermediate": 0.60, # 中級玩家仍需謹慎
# "advanced": 1.0 # 資深者能完全勝任
# },
# "Moderate": {
# "beginner": 0.35, # 適中難度對新手仍具挑戰
# "intermediate": 0.80, # 中級玩家較適合
# "advanced": 1.0 # 資深者完全勝任
# },
# "Low": {
# "beginner": 0.90, # 新手友善品種
# "intermediate": 0.95, # 中級玩家幾乎完全勝任
# "advanced": 1.0 # 資深者完全勝任
# }
# }
# # 取得基礎分數
# score = base_scores.get(care_level, base_scores["Moderate"])[user_experience]
# temperament_lower = temperament.lower()
# temperament_adjustments = 0.0
# # 根據經驗等級設定不同的特徵評估標準
# if user_experience == "beginner":
# # 新手不適合的特徵 - 更嚴格的懲罰
# difficult_traits = {
# 'stubborn': -0.30, # 固執性格嚴重影響新手
# 'independent': -0.25, # 獨立性高的品種不適合新手
# 'dominant': -0.25, # 支配性強的品種需要經驗處理
# 'strong-willed': -0.20, # 強勢性格需要技巧管理
# 'protective': -0.20, # 保護性強需要適當訓練
# 'aloof': -0.15, # 冷漠性格需要耐心培養
# 'energetic': -0.15, # 活潑好動需要經驗引導
# 'aggressive': -0.35 # 攻擊傾向極不適合新手
# }
# # 新手友善的特徵 - 適度的獎勵
# easy_traits = {
# 'gentle': 0.05, # 溫和性格適合新手
# 'friendly': 0.05, # 友善性格容易相處
# 'eager to please': 0.08, # 願意服從較容易訓練
# 'patient': 0.05, # 耐心的特質有助於建立關係
# 'adaptable': 0.05, # 適應性強較容易照顧
# 'calm': 0.06 # 冷靜的性格較好掌握
# }
# # 計算特徵調整
# for trait, penalty in difficult_traits.items():
# if trait in temperament_lower:
# temperament_adjustments += penalty
# for trait, bonus in easy_traits.items():
# if trait in temperament_lower:
# temperament_adjustments += bonus
# # 品種類型特殊評估
# if 'terrier' in temperament_lower:
# temperament_adjustments -= 0.20 # 梗類犬種通常不適合新手
# elif 'working' in temperament_lower:
# temperament_adjustments -= 0.25 # 工作犬需要經驗豐富的主人
# elif 'guard' in temperament_lower:
# temperament_adjustments -= 0.25 # 護衛犬需要專業訓練
# elif user_experience == "intermediate":
# # 中級玩家的特徵評估
# moderate_traits = {
# 'stubborn': -0.15, # 仍然需要注意,但懲罰較輕
# 'independent': -0.10,
# 'intelligent': 0.08, # 聰明的特質可以好好發揮
# 'athletic': 0.06, # 運動能力可以適當訓練
# 'versatile': 0.07, # 多功能性可以開發
# 'protective': -0.08 # 保護性仍需注意
# }
# for trait, adjustment in moderate_traits.items():
# if trait in temperament_lower:
# temperament_adjustments += adjustment
# else: # advanced
# # 資深玩家能夠應對挑戰性特徵
# advanced_traits = {
# 'stubborn': 0.05, # 困難特徵反而成為優勢
# 'independent': 0.05,
# 'intelligent': 0.10,
# 'protective': 0.05,
# 'strong-willed': 0.05
# }
# for trait, bonus in advanced_traits.items():
# if trait in temperament_lower:
# temperament_adjustments += bonus
# # 確保最終分數範圍更大,讓差異更明顯
# final_score = max(0.05, min(1.0, score + temperament_adjustments))
# return final_score
def calculate_experience_score(breed_info: dict, user_prefs: UserPreferences) -> float:
"""
計算飼主經驗與品種需求的匹配分數
這個函數實現了一個全面的經驗評分系統,考慮:
1. 品種的基本照護難度
2. 飼主的經驗水平
3. 特殊照護需求(如健康問題、行為訓練)
4. 時間投入與生活方式的匹配
5. 家庭環境對照護的影響
特別注意:
- 新手飼主面對高難度品種時的顯著降分
- 資深飼主照顧簡單品種的微幅降分
- 特殊需求品種的額外評估
Parameters:
-----------
breed_info: 包含品種特徵的字典
user_prefs: 使用者偏好設定
Returns:
--------
float: 0.0-1.0 之間的匹配分數
"""
care_level = breed_info.get('Care Level', 'MODERATE').upper()
temperament = breed_info.get('Temperament', '').lower()
health_issues = breed_info.get('Health Issues', '').lower()
# 基礎照護難度評分矩陣
base_experience_scores = {
"HIGH": {
"beginner": 0.30, # 高難度品種對新手極具挑戰
"intermediate": 0.70, # 中級飼主需要額外努力
"advanced": 0.95 # 資深飼主最適合
},
"MODERATE": {
"beginner": 0.60, # 中等難度對新手有一定挑戰
"intermediate": 0.85, # 中級飼主較適合
"advanced": 0.90 # 資深飼主可能稍嫌簡單
},
"LOW": {
"beginner": 0.90, # 低難度適合新手
"intermediate": 0.85, # 中級飼主可能感覺無趣
"advanced": 0.80 # 資深飼主可能缺乏挑戰
}
}
# 取得基礎經驗分數
base_score = base_experience_scores.get(care_level,
base_experience_scores["MODERATE"])[user_prefs.experience_level]
# 時間可用性評估
time_adjustments = {
"limited": {
"HIGH": -0.20,
"MODERATE": -0.15,
"LOW": -0.10
},
"moderate": {
"HIGH": -0.10,
"MODERATE": -0.05,
"LOW": 0
},
"flexible": {
"HIGH": 0,
"MODERATE": 0.05,
"LOW": 0.10
}
}
time_adjustment = time_adjustments[user_prefs.time_availability][care_level]
# 行為特徵評估
def evaluate_temperament(temp: str, exp_level: str) -> float:
"""評估品種性格特徵與飼主經驗的匹配度"""
score = 0
# 困難特徵評估
difficult_traits = {
'stubborn': {'beginner': -0.20, 'intermediate': -0.10, 'advanced': 0},
'independent': {'beginner': -0.15, 'intermediate': -0.08, 'advanced': 0},
'dominant': {'beginner': -0.20, 'intermediate': -0.10, 'advanced': -0.05},
'aggressive': {'beginner': -0.25, 'intermediate': -0.15, 'advanced': -0.10}
}
# 友善特徵評估
friendly_traits = {
'friendly': {'beginner': 0.10, 'intermediate': 0.05, 'advanced': 0},
'gentle': {'beginner': 0.10, 'intermediate': 0.05, 'advanced': 0},
'easy to train': {'beginner': 0.15, 'intermediate': 0.10, 'advanced': 0.05}
}
# 計算特徵分數
for trait, penalties in difficult_traits.items():
if trait in temp:
score += penalties[exp_level]
for trait, bonuses in friendly_traits.items():
if trait in temp:
score += bonuses[exp_level]
return score
temperament_adjustment = evaluate_temperament(temperament, user_prefs.experience_level)
# 健康問題評估
def evaluate_health_needs(health: str, exp_level: str) -> float:
"""評估健康問題的照護難度"""
score = 0
serious_conditions = ['hip dysplasia', 'heart disease', 'cancer']
moderate_conditions = ['allergies', 'skin problems', 'ear infections']
# 根據經驗等級調整健康問題的影響
health_impact = {
'beginner': {'serious': -0.20, 'moderate': -0.10},
'intermediate': {'serious': -0.15, 'moderate': -0.05},
'advanced': {'serious': -0.10, 'moderate': -0.03}
}
for condition in serious_conditions:
if condition in health:
score += health_impact[exp_level]['serious']
for condition in moderate_conditions:
if condition in health:
score += health_impact[exp_level]['moderate']
return score
health_adjustment = evaluate_health_needs(health_issues, user_prefs.experience_level)
# 家庭環境考量
family_adjustment = 0
if user_prefs.has_children:
if user_prefs.children_age == 'toddler':
if user_prefs.experience_level == 'beginner':
family_adjustment -= 0.15
elif user_prefs.experience_level == 'intermediate':
family_adjustment -= 0.10
elif user_prefs.children_age == 'school_age':
if user_prefs.experience_level == 'beginner':
family_adjustment -= 0.10
# 生活方式匹配度
lifestyle_adjustments = {
'sedentary': -0.10 if care_level == 'HIGH' else 0,
'moderate': 0,
'active': 0.10 if care_level in ['HIGH', 'MODERATE'] else 0
}
lifestyle_adjustment = lifestyle_adjustments[user_prefs.lifestyle_activity]
# 整合所有評分因素
final_score = base_score + time_adjustment + temperament_adjustment + \
health_adjustment + family_adjustment + lifestyle_adjustment
# 確保最終分數在合理範圍內
return max(0.15, min(1.0, final_score))
def calculate_health_score(breed_name: str, user_prefs: UserPreferences) -> float:
"""
計算品種健康分數,加強健康問題的影響力和與使用者敏感度的連結
重要改進:
1. 根據使用者的健康敏感度調整分數
2. 更嚴格的健康問題評估
3. 考慮多重健康問題的累積效應
4. 加入遺傳疾病的特別考量
"""
if breed_name not in breed_health_info:
return 0.5
health_notes = breed_health_info[breed_name]['health_notes'].lower()
# 嚴重健康問題 - 加重扣分
severe_conditions = {
'hip dysplasia': -0.25, # 髖關節發育不良,影響生活品質
'heart disease': -0.25, # 心臟疾病,需要長期治療
'progressive retinal atrophy': -0.20, # 進行性視網膜萎縮,導致失明
'bloat': -0.22, # 胃扭轉,致命風險
'epilepsy': -0.20, # 癲癇,需要長期藥物控制
'degenerative myelopathy': -0.20, # 脊髓退化,影響行動能力
'von willebrand disease': -0.18 # 血液凝固障礙
}
# 中度健康問題 - 適度扣分
moderate_conditions = {
'allergies': -0.12, # 過敏問題,需要持續關注
'eye problems': -0.15, # 眼睛問題,可能需要手術
'joint problems': -0.15, # 關節問題,影響運動能力
'hypothyroidism': -0.12, # 甲狀腺功能低下,需要藥物治療
'ear infections': -0.10, # 耳道感染,需要定期清理
'skin issues': -0.12 # 皮膚問題,需要特殊護理
}
# 輕微健康問題 - 輕微扣分
minor_conditions = {
'dental issues': -0.08, # 牙齒問題,需要定期護理
'weight gain tendency': -0.08, # 易胖體質,需要控制飲食
'minor allergies': -0.06, # 輕微過敏,可控制
'seasonal allergies': -0.06 # 季節性過敏
}
# 計算基礎健康分數
health_score = 1.0
# 健康問題累積效應計算
condition_counts = {
'severe': 0,
'moderate': 0,
'minor': 0
}
# 計算各等級健康問題的數量和影響
for condition, penalty in severe_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['severe'] += 1
for condition, penalty in moderate_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['moderate'] += 1
for condition, penalty in minor_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['minor'] += 1
# 多重問題的額外懲罰(累積效應)
if condition_counts['severe'] > 1:
health_score *= (0.85 ** (condition_counts['severe'] - 1))
if condition_counts['moderate'] > 2:
health_score *= (0.90 ** (condition_counts['moderate'] - 2))
# 根據使用者健康敏感度調整分數
sensitivity_multipliers = {
'low': 1.1, # 較不在意健康問題
'medium': 1.0, # 標準評估
'high': 0.85 # 非常注重健康問題
}
health_score *= sensitivity_multipliers.get(user_prefs.health_sensitivity, 1.0)
# 壽命影響評估
try:
lifespan = breed_health_info[breed_name].get('average_lifespan', '10-12')
years = float(lifespan.split('-')[0])
if years < 8:
health_score *= 0.85 # 短壽命顯著降低分數
elif years < 10:
health_score *= 0.92 # 較短壽命輕微降低分數
elif years > 13:
health_score *= 1.1 # 長壽命適度加分
except:
pass
# 特殊健康優勢
if 'generally healthy' in health_notes or 'hardy breed' in health_notes:
health_score *= 1.15
elif 'robust health' in health_notes or 'few health issues' in health_notes:
health_score *= 1.1
# 確保分數在合理範圍內,但允許更大的分數差異
return max(0.1, min(1.0, health_score))
# def calculate_noise_score(breed_name: str, user_prefs: UserPreferences) -> float:
# """
# 計算品種噪音分數,特別加強噪音程度與生活環境的關聯性評估
# """
# if breed_name not in breed_noise_info:
# return 0.5
# noise_info = breed_noise_info[breed_name]
# noise_level = noise_info['noise_level'].lower()
# noise_notes = noise_info['noise_notes'].lower()
# # 重新設計基礎噪音分數矩陣,考慮不同情境下的接受度
# base_scores = {
# 'low': {
# 'low': 1.0, # 安靜的狗對低容忍完美匹配
# 'medium': 0.95, # 安靜的狗對一般容忍很好
# 'high': 0.90 # 安靜的狗對高容忍當然可以
# },
# 'medium': {
# 'low': 0.60, # 一般吠叫對低容忍較困難
# 'medium': 0.90, # 一般吠叫對一般容忍可接受
# 'high': 0.95 # 一般吠叫對高容忍很好
# },
# 'high': {
# 'low': 0.25, # 愛叫的狗對低容忍極不適合
# 'medium': 0.65, # 愛叫的狗對一般容忍有挑戰
# 'high': 0.90 # 愛叫的狗對高容忍可以接受
# },
# 'varies': {
# 'low': 0.50, # 不確定的情況對低容忍風險較大
# 'medium': 0.75, # 不確定的情況對一般容忍可嘗試
# 'high': 0.85 # 不確定的情況對高容忍問題較小
# }
# }
# # 取得基礎分數
# base_score = base_scores.get(noise_level, {'low': 0.6, 'medium': 0.75, 'high': 0.85})[user_prefs.noise_tolerance]
# # 吠叫原因評估,根據環境調整懲罰程度
# barking_penalties = {
# 'separation anxiety': {
# 'apartment': -0.30, # 在公寓對鄰居影響更大
# 'house_small': -0.25,
# 'house_large': -0.20
# },
# 'excessive barking': {
# 'apartment': -0.25,
# 'house_small': -0.20,
# 'house_large': -0.15
# },
# 'territorial': {
# 'apartment': -0.20, # 在公寓更容易被觸發
# 'house_small': -0.15,
# 'house_large': -0.10
# },
# 'alert barking': {
# 'apartment': -0.15, # 公寓環境刺激較多
# 'house_small': -0.10,
# 'house_large': -0.08
# },
# 'attention seeking': {
# 'apartment': -0.15,
# 'house_small': -0.12,
# 'house_large': -0.10
# }
# }
# # 計算環境相關的吠叫懲罰
# living_space = user_prefs.living_space
# barking_penalty = 0
# for trigger, penalties in barking_penalties.items():
# if trigger in noise_notes:
# barking_penalty += penalties.get(living_space, -0.15)
# # 特殊情況評估
# special_adjustments = 0
# if user_prefs.has_children:
# # 孩童年齡相關調整
# child_age_adjustments = {
# 'toddler': {
# 'high': -0.20, # 幼童對吵鬧更敏感
# 'medium': -0.15,
# 'low': -0.05
# },
# 'school_age': {
# 'high': -0.15,
# 'medium': -0.10,
# 'low': -0.05
# },
# 'teenager': {
# 'high': -0.10,
# 'medium': -0.05,
# 'low': -0.02
# }
# }
# # 根據孩童年齡和噪音等級調整
# age_adj = child_age_adjustments.get(user_prefs.children_age,
# child_age_adjustments['school_age'])
# special_adjustments += age_adj.get(noise_level, -0.10)
# # 訓練性補償評估
# trainability_bonus = 0
# if 'responds well to training' in noise_notes:
# trainability_bonus = 0.12
# elif 'can be trained' in noise_notes:
# trainability_bonus = 0.08
# elif 'difficult to train' in noise_notes:
# trainability_bonus = 0.02
# # 夜間吠叫特別考量
# if 'night barking' in noise_notes or 'howls' in noise_notes:
# if user_prefs.living_space == 'apartment':
# special_adjustments -= 0.15
# elif user_prefs.living_space == 'house_small':
# special_adjustments -= 0.10
# else:
# special_adjustments -= 0.05
# # 計算最終分數,確保更大的分數範圍
# final_score = base_score + barking_penalty + special_adjustments + trainability_bonus
# return max(0.1, min(1.0, final_score))
def calculate_noise_score(breed_info: dict, user_prefs: UserPreferences) -> float:
"""
計算品種噪音特性與使用者需求的匹配分數
這個函數建立了一個細緻的噪音評估系統,考慮多個關鍵因素:
1. 品種的基本吠叫傾向
2. 居住環境對噪音的敏感度
3. 吠叫的情境和原因
4. 鄰居影響的考量
5. 家庭成員的噪音承受度
6. 訓練可能性的評估
特別注意:
- 公寓環境的嚴格標準
- 有幼童時的特殊考量
- 獨處時間的影響
- 品種的可訓練性
Parameters:
-----------
breed_info: 包含品種特性的字典,包括吠叫傾向和訓練難度
user_prefs: 使用者偏好設定,包含噪音容忍度和環境因素
Returns:
--------
float: 0.0-1.0 之間的匹配分數,分數越高表示噪音特性越符合需求
"""
# 提取基本資訊
noise_level = breed_info.get('Noise Level', 'MODERATE').upper()
barking_tendency = breed_info.get('Barking Tendency', 'MODERATE').upper()
trainability = breed_info.get('Trainability', 'MODERATE').upper()
temperament = breed_info.get('Temperament', '').lower()
# 基礎噪音評分矩陣 - 考慮環境和噪音容忍度
base_noise_scores = {
"LOW": {
"apartment": {
"low": 1.0, # 安靜的狗在公寓最理想
"medium": 0.95,
"high": 0.90
},
"house_small": {
"low": 0.95,
"medium": 0.90,
"high": 0.85
},
"house_large": {
"low": 0.90,
"medium": 0.85,
"high": 0.80 # 太安靜可能不夠警戒
}
},
"MODERATE": {
"apartment": {
"low": 0.60,
"medium": 0.80,
"high": 0.85
},
"house_small": {
"low": 0.70,
"medium": 0.85,
"high": 0.90
},
"house_large": {
"low": 0.75,
"medium": 0.90,
"high": 0.95
}
},
"HIGH": {
"apartment": {
"low": 0.20, # 吵鬧的狗在公寓極不適合
"medium": 0.40,
"high": 0.60
},
"house_small": {
"low": 0.30,
"medium": 0.50,
"high": 0.70
},
"house_large": {
"low": 0.40,
"medium": 0.60,
"high": 0.80
}
}
}
# 取得基礎噪音分數
base_score = base_noise_scores.get(noise_level, base_noise_scores["MODERATE"])\
[user_prefs.living_space][user_prefs.noise_tolerance]
# 吠叫情境評估
def evaluate_barking_context(temp: str, living_space: str) -> float:
"""評估不同情境下的吠叫問題嚴重度"""
context_score = 0
# 不同吠叫原因的權重
barking_contexts = {
'separation anxiety': {
'apartment': -0.25,
'house_small': -0.20,
'house_large': -0.15
},
'territorial': {
'apartment': -0.20,
'house_small': -0.15,
'house_large': -0.10
},
'alert barking': {
'apartment': -0.15,
'house_small': -0.10,
'house_large': -0.05
},
'attention seeking': {
'apartment': -0.15,
'house_small': -0.10,
'house_large': -0.08
}
}
for context, penalties in barking_contexts.items():
if context in temp:
context_score += penalties[living_space]
return context_score
# 計算吠叫情境的影響
barking_context_adjustment = evaluate_barking_context(temperament, user_prefs.living_space)
# 訓練可能性評估
trainability_adjustments = {
"HIGH": 0.10, # 容易訓練可以改善吠叫問題
"MODERATE": 0.05,
"LOW": -0.05 # 難以訓練則較難改善
}
trainability_adjustment = trainability_adjustments.get(trainability, 0)
# 家庭環境考量
family_adjustment = 0
if user_prefs.has_children:
child_age_factors = {
'toddler': -0.20, # 幼童需要安靜環境
'school_age': -0.15,
'teenager': -0.10
}
family_adjustment = child_age_factors.get(user_prefs.children_age, -0.15)
# 根據噪音等級調整影響程度
if noise_level == "HIGH":
family_adjustment *= 1.5
elif noise_level == "LOW":
family_adjustment *= 0.5
# 獨處時間的影響
alone_time_adjustment = 0
if user_prefs.home_alone_time > 6:
if 'separation anxiety' in temperament or noise_level == "HIGH":
alone_time_adjustment = -0.15
elif noise_level == "MODERATE":
alone_time_adjustment = -0.10
# 鄰居影響評估(特別是公寓環境)
neighbor_adjustment = 0
if user_prefs.living_space == "apartment":
if noise_level == "HIGH":
neighbor_adjustment = -0.15
elif noise_level == "MODERATE":
neighbor_adjustment = -0.10
# 樓層因素
if user_prefs.living_floor > 1:
neighbor_adjustment -= min(0.10, (user_prefs.living_floor - 1) * 0.02)
# 整合所有評分因素
final_score = base_score + barking_context_adjustment + trainability_adjustment + \
family_adjustment + alone_time_adjustment + neighbor_adjustment
# 確保最終分數在合理範圍內
return max(0.15, min(1.0, final_score))
# 1. 計算基礎分數
print("\n=== 開始計算品種相容性分數 ===")
print(f"處理品種: {breed_info.get('Breed', 'Unknown')}")
print(f"品種信息: {breed_info}")
print(f"使用者偏好: {vars(user_prefs)}")
# 計算所有基礎分數並整合到字典中
scores = {
'space': calculate_space_score(
breed_info['Size'],
user_prefs.living_space,
user_prefs.yard_access != 'no_yard',
breed_info.get('Exercise Needs', 'Moderate')
),
'exercise': calculate_exercise_score(
breed_info.get('Exercise Needs', 'Moderate'),
user_prefs.exercise_time,
user_prefs.exercise_type
),
'grooming': calculate_grooming_score(
breed_info.get('Grooming Needs', 'Moderate'),
user_prefs.grooming_commitment.lower(),
breed_info['Size']
),
'experience': calculate_experience_score(
breed_info.get('Care Level', 'Moderate'),
user_prefs.experience_level,
breed_info.get('Temperament', '')
),
'health': calculate_health_score(
breed_info.get('Breed', ''),
user_prefs
),
'noise': calculate_noise_score(
breed_info.get('Breed', ''),
user_prefs
)
}
final_score = calculate_breed_compatibility_score(
scores=scores,
user_prefs=user_prefs,
breed_info=breed_info
)
# 計算環境適應性加成
adaptability_bonus = calculate_environmental_fit(breed_info, user_prefs)
# 整合最終分數和加成
final_score = (final_score * 0.9) + (adaptability_bonus * 0.1)
final_score = amplify_score_extreme(final_score)
# 更新並返回完整的評分結果
scores.update({
'overall': final_score,
'adaptability_bonus': adaptability_bonus
})
return scores
except Exception as e:
print(f"\n!!!!! 發生嚴重錯誤 !!!!!")
print(f"錯誤類型: {type(e).__name__}")
print(f"錯誤訊息: {str(e)}")
print(f"完整錯誤追蹤:")
print(traceback.format_exc())
return {k: 0.6 for k in ['space', 'exercise', 'grooming', 'experience', 'health', 'noise', 'overall']}
def calculate_environmental_fit(breed_info: dict, user_prefs: UserPreferences) -> float:
"""計算品種與環境的適應性加成"""
adaptability_score = 0.0
description = breed_info.get('Description', '').lower()
temperament = breed_info.get('Temperament', '').lower()
# 環境適應性評估
if user_prefs.living_space == 'apartment':
if 'adaptable' in temperament or 'apartment' in description:
adaptability_score += 0.1
if breed_info.get('Size') == 'Small':
adaptability_score += 0.05
elif user_prefs.living_space == 'house_large':
if 'active' in temperament or 'energetic' in description:
adaptability_score += 0.1
# 氣候適應性
if user_prefs.climate in description or user_prefs.climate in temperament:
adaptability_score += 0.05
return min(0.2, adaptability_score)
# def calculate_breed_compatibility_score(scores: dict, user_prefs: UserPreferences, breed_info: dict) -> float:
# """
# 改進的品種相容性評分系統
# 通過更細緻的特徵評估和動態權重調整,自然產生分數差異
# """
# # 評估關鍵特徵的匹配度,使用更極端的調整係數
# def evaluate_key_features():
# # 空間適配性評估
# space_multiplier = 1.0
# if user_prefs.living_space == 'apartment':
# if breed_info['Size'] == 'Giant':
# space_multiplier = 0.3 # 嚴重不適合
# elif breed_info['Size'] == 'Large':
# space_multiplier = 0.4 # 明顯不適合
# elif breed_info['Size'] == 'Small':
# space_multiplier = 1.4 # 明顯優勢
# # 運動需求評估
# exercise_multiplier = 1.0
# exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper()
# if exercise_needs == 'VERY HIGH':
# if user_prefs.exercise_time < 60:
# exercise_multiplier = 0.3 # 嚴重不足
# elif user_prefs.exercise_time > 150:
# exercise_multiplier = 1.5 # 完美匹配
# elif exercise_needs == 'LOW' and user_prefs.exercise_time > 150:
# exercise_multiplier = 0.5 # 運動過度
# return space_multiplier, exercise_multiplier
# # 計算經驗匹配度
# def evaluate_experience():
# exp_multiplier = 1.0
# care_level = breed_info.get('Care Level', 'MODERATE')
# if care_level == 'High':
# if user_prefs.experience_level == 'beginner':
# exp_multiplier = 0.4
# elif user_prefs.experience_level == 'advanced':
# exp_multiplier = 1.3
# elif care_level == 'Low':
# if user_prefs.experience_level == 'advanced':
# exp_multiplier = 0.9 # 略微降低評分,因為可能不夠有挑戰性
# return exp_multiplier
# # 取得特徵調整係數
# space_mult, exercise_mult = evaluate_key_features()
# exp_mult = evaluate_experience()
# # 調整基礎分數
# adjusted_scores = {
# 'space': scores['space'] * space_mult,
# 'exercise': scores['exercise'] * exercise_mult,
# 'experience': scores['experience'] * exp_mult,
# 'grooming': scores['grooming'],
# 'health': scores['health'],
# 'noise': scores['noise']
# }
# # 計算加權平均,關鍵特徵佔更大權重
# weights = {
# 'space': 0.35,
# 'exercise': 0.30,
# 'experience': 0.20,
# 'grooming': 0.15,
# 'health': 0.10,
# 'noise': 0.10
# }
# # 動態調整權重
# if user_prefs.living_space == 'apartment':
# weights['space'] *= 1.5
# weights['noise'] *= 1.3
# if abs(user_prefs.exercise_time - 120) > 60: # 運動時間極端情況
# weights['exercise'] *= 1.4
# # 正規化權重
# total_weight = sum(weights.values())
# normalized_weights = {k: v/total_weight for k, v in weights.items()}
# # 計算最終分數
# final_score = sum(adjusted_scores[k] * normalized_weights[k] for k in scores.keys())
# # 品種特性加成
# breed_bonus = calculate_breed_bonus(breed_info, user_prefs)
# # 整合最終分數,保持在0-1範圍內
# return min(1.0, max(0.0, (final_score * 0.85) + (breed_bonus * 0.15)))
def calculate_compatibility_score(scores: dict, user_prefs: UserPreferences, breed_info: dict) -> float:
"""
計算品種與使用者的整體相容性分數
這是推薦系統的核心評分函數,負責:
1. 智能整合各面向評分
2. 動態調整評分權重
3. 處理關鍵條件的優先級
4. 產生最終的匹配分數
評分策略:
- 基礎分數:由各項指標的加權平均獲得
- 動態權重:根據用戶情況動態調整各項權重
- 關鍵條件:某些條件不滿足會顯著降低總分
- 加成系統:特殊匹配會提供額外加分
Parameters:
-----------
scores: 包含各項評分的字典
user_prefs: 使用者偏好設定
breed_info: 品種特性信息
Returns:
--------
float: 60.0-95.0 之間的最終匹配分數
"""
def calculate_dynamic_weights() -> dict:
"""計算動態權重分配"""
# 基礎權重設定
weights = {
'space': 0.20,
'exercise': 0.20,
'experience': 0.15,
'grooming': 0.15,
'health': 0.15,
'noise': 0.15
}
# 公寓住戶權重調整
if user_prefs.living_space == "apartment":
weights['space'] *= 1.3
weights['noise'] *= 1.3
weights['exercise'] *= 0.8
# 有幼童時的權重調整
if user_prefs.has_children and user_prefs.children_age == 'toddler':
weights['experience'] *= 1.3
weights['noise'] *= 1.2
weights['health'] *= 1.2
# 新手飼主的權重調整
if user_prefs.experience_level == 'beginner':
weights['experience'] *= 1.4
weights['health'] *= 1.2
weights['grooming'] *= 1.2
# 健康敏感度的權重調整
if user_prefs.health_sensitivity == 'high':
weights['health'] *= 1.3
# 運動時間極端情況的權重調整
if abs(user_prefs.exercise_time - 120) > 60:
weights['exercise'] *= 1.3
# 正規化權重
total = sum(weights.values())
return {k: v/total for k, v in weights.items()}
def calculate_critical_factors() -> float:
"""評估關鍵因素的影響"""
critical_score = 1.0
# 空間關鍵條件
if user_prefs.living_space == "apartment":
if breed_info['Size'] == 'Giant':
critical_score *= 0.7
elif breed_info['Size'] == 'Large':
critical_score *= 0.8
# 運動需求關鍵條件
exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper()
if exercise_needs == 'VERY HIGH' and user_prefs.exercise_time < 60:
critical_score *= 0.75
elif exercise_needs == 'HIGH' and user_prefs.exercise_time < 45:
critical_score *= 0.8
# 新手飼主關鍵條件
if user_prefs.experience_level == 'beginner':
if 'aggressive' in breed_info.get('Temperament', '').lower():
critical_score *= 0.7
elif 'dominant' in breed_info.get('Temperament', '').lower():
critical_score *= 0.8
# 噪音關鍵條件
if user_prefs.living_space == "apartment" and \
breed_info.get('Noise Level', 'MODERATE').upper() == 'HIGH' and \
user_prefs.noise_tolerance == 'low':
critical_score *= 0.7
return critical_score
def calculate_bonus_factors() -> float:
"""計算額外加分因素"""
bonus = 1.0
temperament = breed_info.get('Temperament', '').lower()
# 完美匹配加分
perfect_matches = 0
for score in scores.values():
if score > 0.9:
perfect_matches += 1
if perfect_matches >= 3:
bonus += 0.05
# 特殊匹配加分
if user_prefs.has_children and 'good with children' in temperament:
bonus += 0.03
if user_prefs.living_space == "apartment" and 'adaptable' in temperament:
bonus += 0.03
if user_prefs.experience_level == 'beginner' and 'easy to train' in temperament:
bonus += 0.03
return min(1.15, bonus)
# 計算動態權重
weights = calculate_dynamic_weights()
# 計算基礎加權分數
base_score = sum(scores[k] * weights[k] for k in scores.keys())
# 應用關鍵因素
critical_factor = calculate_critical_factors()
# 計算加分
bonus_factor = calculate_bonus_factors()
# 計算最終原始分數
raw_score = base_score * critical_factor * bonus_factor
# 轉換為最終分數(60-95範圍)
final_score = 60 + (raw_score * 35)
# 確保分數在合理範圍內並保留兩位小數
return round(max(60.0, min(95.0, final_score)), 2)
# def amplify_score_extreme(score: float) -> float:
# """
# 改進的分數轉換函數
# 提供更大的分數範圍和更明顯的差異
# 轉換邏輯:
# - 極差匹配 (0.0-0.3) -> 60-68%
# - 較差匹配 (0.3-0.5) -> 68-75%
# - 中等匹配 (0.5-0.7) -> 75-85%
# - 良好匹配 (0.7-0.85) -> 85-92%
# - 優秀匹配 (0.85-1.0) -> 92-95%
# """
# if score < 0.3:
# # 極差匹配:快速線性增長
# return 0.60 + (score / 0.3) * 0.08
# elif score < 0.5:
# # 較差匹配:緩慢增長
# position = (score - 0.3) / 0.2
# return 0.68 + position * 0.07
# elif score < 0.7:
# # 中等匹配:穩定線性增長
# position = (score - 0.5) / 0.2
# return 0.75 + position * 0.10
# elif score < 0.85:
# # 良好匹配:加速增長
# position = (score - 0.7) / 0.15
# return 0.85 + position * 0.07
# else:
# # 優秀匹配:最後衝刺
# position = (score - 0.85) / 0.15
# return 0.92 + position * 0.03
def amplify_score_extreme(score: float) -> float:
"""
將原始相容性分數(0-1)轉換為最終評分(60-95)
這個函數負責:
1. 將內部計算的原始分數轉換為更有意義的最終分數
2. 確保分數分布更自然且有區別性
3. 突出極佳和極差的匹配
4. 避免分數過度集中在中間區域
轉換策略:
- 極佳匹配(0.85-1.0):轉換為 90-95 分
- 優良匹配(0.70-0.85):轉換為 85-90 分
- 良好匹配(0.55-0.70):轉換為 75-85 分
- 一般匹配(0.40-0.55):轉換為 70-75 分
- 勉強匹配(0.25-0.40):轉換為 65-70 分
- 不推薦匹配(0-0.25):轉換為 60-65 分
Parameters:
-----------
score: 原始相容性分數(0.0-1.0)
Returns:
--------
float: 轉換後的最終分數(60.0-95.0)
"""
# 使用分段函數進行更自然的轉換
if score >= 0.85:
# 極佳匹配:90-95分
position = (score - 0.85) / 0.15
return 90.0 + (position * 5.0)
elif score >= 0.70:
# 優良匹配:85-90分
position = (score - 0.70) / 0.15
return 85.0 + (position * 5.0)
elif score >= 0.55:
# 良好匹配:75-85分
position = (score - 0.55) / 0.15
return 75.0 + (position * 10.0)
elif score >= 0.40:
# 一般匹配:70-75分
position = (score - 0.40) / 0.15
return 70.0 + (position * 5.0)
elif score >= 0.25:
# 勉強匹配:65-70分
position = (score - 0.25) / 0.15
return 65.0 + (position * 5.0)
else:
# 不推薦匹配:60-65分
position = score / 0.25
return 60.0 + (position * 5.0)