如何拆解DataFrame中嵌套字典列表并提取指定字段?
解决方案
错误原因
TypeError: 'float' object is not iterable 是因为你的priceHistory列中存在非列表类型的值(比如空字符串、NaN,在pandas中NaN会被识别为float类型),当lambda尝试遍历这些非列表值时触发报错。
分步解决代码
1. 预处理priceHistory列
先统一列内数据类型,把所有非列表的值转换为空列表,避免遍历出错:
import pandas as pd # 你的原始DataFrame df = pd.DataFrame( [[19, [{'priceChangeRate': 0, 'date': '2015-05-29', 'source': 'Public Record', 'postingIsRental': False, 'time': 1432857600000, 'sellerAgent': None, 'showCountyLink': False, 'attributeSource': {'infoString2': 'Public Record', 'infoString3': None, 'infoString1': None}, 'pricePerSquareFoot': 275, 'buyerAgent': None, 'event': 'Sold', 'price': 877205}], ['Low flow commode', 'Low flow fixtures', 'Water-Smart Landscaping'],''], [89, [{'priceChangeRate': 0.090909090909091, 'date': '2023-07-14', 'source': 'Public Record', 'postingIsRental': False, 'time': 1689292800000, 'sellerAgent': {'name': 'seller1', 'photo': {'url': 'https://sellerphoto1.jpg'}, 'profileUrl': '/profile/sellerprofile1/'}, 'showCountyLink': False, 'attributeSource': {'infoString2': 'Public Record', 'infoString3': None, 'infoString1': None}, 'pricePerSquareFoot': 308, 'buyerAgent': {'name': 'buyer1', 'photo': {'url': 'https://buyerphoto1.jpg'}, 'profileUrl': '/profile/buyerprofile1/'}, 'event': 'Sold', 'price': 1200000}, {'priceChangeRate': 0, 'date': '2015-08-20', 'source': 'Public Record', 'postingIsRental': False, 'time': 1440028800000, 'sellerAgent': None, 'showCountyLink': False, 'attributeSource': {'infoString2': 'Public Record', 'infoString3': None, 'infoString1': None}, 'pricePerSquareFoot': 50, 'buyerAgent': None, 'event': 'Sold', 'price': 195000}],'', ['Windows', 'Insulation', 'HVAC', 'Appliances', 'Lighting']]], columns=['id', 'priceHistory', 'WaterConservation', 'EnergyEfficient']) # 预处理priceHistory:非列表值转为空列表 df['priceHistory'] = df['priceHistory'].apply(lambda x: x if isinstance(x, list) else [])
2. 拆解priceHistory中的date和price
根据业务需求选择以下两种方式:
方式一:展开所有历史记录(多行)
把每行的多个价格历史拆分成单独的行,保留所有记录:
# 将priceHistory列表展开为多行 price_expanded = df.explode('priceHistory', ignore_index=True) # 从字典中提取date和price字段 price_expanded[['date', 'price']] = price_expanded['priceHistory'].apply( lambda d: pd.Series([d.get('date'), d.get('price')]) if d else pd.Series([None, None]) ) # 可删除原priceHistory列 price_expanded = price_expanded.drop('priceHistory', axis=1) print(price_expanded)
方式二:提取每行最新的价格记录(单行)
如果只需要每行的最新价格(按日期排序取最后一条):
def extract_latest_price(history_list): if not history_list: return pd.Series([None, None]) # 按日期排序,取最后一条记录 sorted_history = sorted(history_list, key=lambda x: x.get('date', '')) latest_record = sorted_history[-1] return pd.Series([latest_record.get('date'), latest_record.get('price')]) # 添加最新日期和价格列 df[['latest_date', 'latest_price']] = df['priceHistory'].apply(extract_latest_price) print(df)
3. 处理WaterConservation和EnergyEfficient列
根据需求选择:
方式一:转成逗号分隔字符串
把列表格式的内容转为可读性更强的字符串:
df['WaterConservation'] = df['WaterConservation'].apply( lambda x: ', '.join(x) if isinstance(x, list) else str(x).strip() ) df['EnergyEfficient'] = df['EnergyEfficient'].apply( lambda x: ', '.join(x) if isinstance(x, list) else str(x).strip() )
方式二:展开为多行
把每个列表条目拆分成单独的行:
# 分别展开节水、节能列 water_expanded = df.explode('WaterConservation', ignore_index=True) energy_expanded = df.explode('EnergyEfficient', ignore_index=True) # 同时展开两个列(会产生笛卡尔积,需根据业务场景选择) combined_expanded = df.explode('WaterConservation').explode('EnergyEfficient', ignore_index=True)
内容的提问来源于stack exchange,提问作者Kerry Harp
相关产品推荐
相关产品推荐

