Python餐厅推荐系统向量维度不匹配ValueError问题求助
餐厅推荐系统维度不匹配ValueError问题排查与解决
我用Python开发了一款结合用户偏好(菜系、价位)和余弦相似度的餐厅推荐系统,当前碰到ValueError: User preference vector and restaurant feature vectors must have the same number of dimensions错误,两种场景都会触发:
- 场景1:餐厅数据用one-hot编码处理菜系,用户选择“所有菜系”(生成全1的one-hot向量)时报错
- 场景2:用户选择特定菜系(生成仅单一特征为1的one-hot向量)时同样报错
我怀疑是用户偏好向量与餐厅数据中的菜系表示不匹配(比如单菜系与多菜系列表的差异),以下是相关代码与报错栈信息:
一、用户偏好函数
# 两个重载的get_user_preferences函数 def get_user_preferences(cuisine=None, price_range=None): # 初始化空的偏好向量 user_pref = [0] * len(encoder.get_feature_names_out()) # 设置菜系偏好(one-hot编码) if cuisine: cuisine_index = encoder.get_feature_names_out().tolist().index(cuisine) user_pref[cuisine_index] = 1 # 设置价位 if price_range: user_pref.append(price_range) # 假设价位为数值型 return user_pref def get_user_preferences(cuisine=None, price_range=None): """ 返回:包含one-hot编码菜系与价位的用户偏好向量列表。 """ # 初始化对所有菜系偏好均等的空向量 num_cuisines = len(encoder.get_feature_names_out()) user_pref = [1 / num_cuisines] * num_cuisines # 设置菜系偏好(one-hot编码) if cuisine: cuisine_index = encoder.get_feature_names_out().tolist().index(cuisine) user_pref[cuisine_index] = 1 # 设置价位 if price_range: user_pref.append(price_range) # 假设价位为数值型 return user_pref
二、余弦相似度与用户输入函数
def cosine_similarity(user_pref, restaurant_vec): # 从用户偏好与餐厅向量中提取菜系向量 user_pref_cuisine = user_pref[:-1] if len(user_pref) > 1 else user_pref # 若有则排除价位 restaurant_vec_cuisine = restaurant_vec[:-1] if len(restaurant_vec) > 1 else restaurant_vec # 检查用户菜系向量与餐厅菜系向量长度是否一致 if len(user_pref_cuisine) != len(restaurant_vec_cuisine): raise ValueError("User preference cuisine vector and restaurant cuisine vector must have the same number of dimensions.") # 计算余弦相似度 return np.dot(user_pref_cuisine, restaurant_vec_cuisine) / (np.linalg.norm(user_pref_cuisine) * np.linalg.norm(restaurant_vec_cuisine)) def get_user_preferences_from_user(): """ 该函数提示用户选择偏好的菜系与价位。 返回:包含one-hot编码菜系与价位的用户偏好向量列表。 """ # 获取用户菜系输入 available_cuisines = list(df['Cuisines'].unique()) # 假设唯一菜系存储在某列中 while True: cuisine = input("Enter your preferred cuisine (or 'all' for any): ").lower().strip() if cuisine in available_cuisines or cuisine == 'all': break else: print(f"Invalid cuisine. Available options are: {', '.join(available_cuisines)}") # 获取用户价位输入 price_ranges = { "1": "Cheap", "2": "Moderate", "3": "Fine Dining" } while True: price_range = input("Enter your preferred price range (1 - Cheap, 2 - Moderate, 3 - Fine Dining): ") if price_range in price_ranges: break else: print(f"Invalid price range. Please enter 1, 2, or 3.") # 编码菜系(非“所有”时) if cuisine != 'all': cuisine_index = encoder.get_feature_names_out().tolist().index(cuisine) user_pref = [0] * len(encoder.get_feature_names_out()) user_pref[cuisine_index] = 1 else: user_pref = [1] * len(encoder.get_feature_names_out()) # 对所有菜系进行one-hot编码 # 若指定则添加价位 if price_range: user_pref.append(int(price_range)) # 确保用户偏好向量与餐厅特征向量的特征数量一致 if len(user_pref) != len(df.drop(columns=['Restaurant Name']).values[0]): raise ValueError("User preference vector and restaurant feature vectors must have the same number of dimensions.") return user_pref
三、餐厅推荐函数
def recommend_restaurants(user_pref, df=df, top_n=5): """ 基于用户偏好与相似度分数推荐餐厅。 参数: user_pref (list): 用户偏好向量。 df (pandas.DataFrame, 可选): 预处理后的餐厅数据,默认值为df。 top_n (int, 可选): 返回的顶级推荐数量,默认值为5。 返回: pandas.DataFrame: 包含详细信息的前N个推荐餐厅。 """ # 构建包含编码菜系的餐厅特征向量 restaurant_feature_vectors = df.drop(columns=['Restaurant Name']).values # 检查用户偏好向量与餐厅特征向量的维度是否一致 if len(user_pref) != len(restaurant_feature_vectors[0]): raise ValueError("User preference vector and restaurant feature vectors must have the same number of dimensions.") # 计算每个餐厅的相似度 similarities = np.array([cosine_similarity(user_pref, vec) for vec in restaurant_feature_vectors]) # 按相似度降序排序 df_sorted = df.assign(similarity=similarities).sort_values(by='similarity', ascending=False) # 返回前N个结果 return df_sorted.head(top_n).drop('similarity', axis=1)
调用代码与报错信息
调用代码
# 获取用户偏好 user_pref = get_user_preferences_from_user() # 生成推荐 recommendations = recommend_restaurants(user_pref, df=df) print("Top Restaurant Recommendations:") print(recommendations)
用户输入
Enter your preferred cuisine (or 'all' for any): all Enter your preferred price range (1 - Cheap, 2 - Moderate, 3 - Fine Dining): 4 Invalid price range. Please enter 1, 2, or 3. Enter your preferred price range (1 - Cheap, 2 - Moderate, 3 - Fine Dining): 1
报错栈
ValueError Traceback (most recent call last) in <cell line: 2>() 1 # 假设存在获取用户菜系与价位输入的函数 ----> 2 user_pref = get_user_preferences_from_user() # 替换为你的函数 3 4 # 调用推荐函数并传入数据与用户偏好 5 recommendations = recommend_restaurants(user_pref, df=df) # 使用你的df数据 in get_user_preferences_from_user() 42 # 确保用户偏好向量与餐厅特征向量的特征数量一致 43 if len(user_pref) != len(df.drop(columns=['Restaurant Name']).values[0]): ---> 44 raise ValueError("User preference vector and restaurant feature vectors must have the same number of dimensions.") 45 46 return user_pref ValueError: User preference vector and restaurant feature vectors must have the same number of dimensions.
问题排查与解决方案
核心原因
- 向量维度对齐错误:用户偏好向量是先构建one-hot菜系向量,再追加价位特征;但餐厅数据的特征向量中,价位是作为固定列存在的,不是追加项。若餐厅数据特征列数为
菜系one-hot列数 + 1(价位),但用户向量构建逻辑导致长度不匹配,就会触发错误。 - 菜系编码范围不匹配:
encoder.get_feature_names_out()生成的菜系列,与df中实际的one-hot编码菜系列数不一致(比如餐厅存在多菜系标记,one-hot后列数更多)。
修复步骤
步骤1:确认特征列结构
先打印餐厅数据的特征列信息,验证维度是否匹配:
# 打印餐厅特征列与encoder生成的菜系列 print("餐厅特征列:", df.drop(columns=['Restaurant Name']).columns.tolist()) print("Encoder生成的菜系列:", encoder.get_feature_names_out().tolist()) print("餐厅特征列数:", len(df.drop(columns=['Restaurant Name']).columns)) print("Encoder菜系列数:", len(encoder.get_feature_names_out()))
确保餐厅特征列数 = 菜系one-hot列数 + 价位列数(1列)。
步骤2:修正用户偏好向量构建逻辑
不要直接追加价位,而是将价位放在与餐厅数据对应的列位置:
def get_user_preferences_from_user(): # ... 保留前面的用户输入逻辑 ... # 初始化偏好向量,长度与餐厅特征列一致 feature_cols = df.drop(columns=['Restaurant Name']).columns.tolist() user_pref = [0] * len(feature_cols) # 填充菜系部分 cuisine_cols = encoder.get_feature_names_out().tolist() if cuisine != 'all': # 找到目标菜系在特征列中的索引并赋值 for idx, col in enumerate(feature_cols): if col == cuisine: user_pref[idx] = 1 break else: # 所有菜系列赋值为1 for idx, col in enumerate(feature_cols): if col in cuisine_cols: user_pref[idx] = 1 # 填充价位部分(假设餐厅数据中价位列名为Price_Range) price_col_idx = feature_cols.index('Price_Range') if 'Price_Range' in feature_cols else None if price_range and price_col_idx is not None: user_pref[price_col_idx] = int(price_range) return user_pref
步骤3:修正余弦相似度计算逻辑
基于菜系列的实际索引提取向量,而非简单截取[:-1]:
def cosine_similarity(user_pref, restaurant_vec): feature_cols = df.drop(columns=['Restaurant Name']).columns.tolist() cuisine_cols = encoder.get_feature_names_out().tolist() # 获取所有菜系列的索引 cuisine_indices = [feature_cols.index(col) for col in cuisine_cols] # 提取菜系向量 user_pref_cuisine = [user_pref[i] for i in cuisine_indices] restaurant_vec_cuisine = [restaurant_vec[i] for i in cuisine_indices] # 计算余弦相似度 return np.dot(user_pref_cuisine, restaurant_vec_cuisine) / (np.linalg.norm(user_pref_cuisine) * np.linalg.norm(restaurant_vec_cuisine))
步骤4:适配多菜系场景
如果餐厅存在多菜系标记(如“中餐, 川菜”),one-hot编码后对应列都会是1。此时用户选“所有菜系”时,所有菜系列设为1,可正常匹配这类餐厅的特征。
内容的提问来源于stack exchange,提问作者user25024734
相关产品推荐
相关产品推荐

