news 2026/9/1 20:38:44

Pandas数据清洗与整形实战:Airbnb房源数据完整处理指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pandas数据清洗与整形实战:Airbnb房源数据完整处理指南

之前处理 Airbnb 房源数据时,最耗时的往往不是建模,而是怎么把一份真实且凌乱的 listing 表弄干净。价格字段带着$和千分位逗号、last_review大量缺失、room_type出现Room/room/Private room多种写法,这些问题不提前处理,后面任何统计、可视化和建模都会跑偏。网上关于 pandas 数据清洗的教程很多,但多数只讲单个知识点,很难直接套到一份完整数据集上。本文将围绕 Cleaning and Shaping the Airbnb Listings 这一主题,整理一套从读取、探查、清洗、整形到导出的完整流程,并补充几个真实项目中常见的报错排查思路。无论你刚入门数据分析,还是准备用 Airbnb 公开数据做课程项目或 Kaggle 练习,都可以直接按这篇文章的步骤走一遍。

1. 背景与核心概念

1.1 什么是 Airbnb Listings 数据

Airbnb Listings 数据,简单理解就是房源列表信息。一份常见的房源表会包含房源编号、房源标题、房东信息、所在区域、房型、每晚价格、最少入住晚数、评论数量、最近评论日期、可订天数、经纬度等字段。如果从 Inside Airbnb 这类第三方公开数据平台下载,通常可以得到单个城市的listings.csv,文件大小从几 MB 到几十 MB 不等,行数从几千到几万都有可能。

这类数据最大的特点是“看起来能直接用,实际上一用就报错”。比如价格列是字符串"$120.00",评论数有时是数字有时是字符串,日期列有的写成2023-06-15,有的写成2022/11/03,甚至还有空值。所以拿到数据后的第一步,不是画图,不是建模,而是先做 data cleaning 和 data shaping。

1.2 数据清洗和数据整形有什么区别

在动手写代码之前,先厘清两个概念。

  • 数据清洗(Data Cleaning):处理缺失值、重复值、异常值、错误格式,目标是让数据“可用”。
  • 数据整形(Data Shaping):调整数据的列名、数据类型、结构、派生字段,目标是让数据“好用”。

清洗解决的是“脏不脏”的问题,整形解决的是“顺不顺手”的问题。实际项目中两者经常连续出现,清洗过程中会顺带做整形,整形过程中也可能发现新的清洗需求。比如把price从字符串转成浮点数,本质上既是清洗也是整形;把last_review统一成datetime类型,同样跨越了两个环节。

1.3 为什么这两步如此重要

真实数据分析项目中,数据清洗和整形通常要占整个项目 50% 到 70% 的时间。如果这一步做得不扎实,模型训练效果会非常差,甚至出现“垃圾进、垃圾出”的情况。反过来说,如果能把这一步标准化、函数化,以后每次拿到新的城市数据,只需要改路径和个别字段名,就能快速产出一份干净的分析底表,这也是本篇文章想帮你达到的目的。

2. 环境准备与版本说明

在开始代码实战前,先确认运行环境。

  • 操作系统:Windows / macOS / Linux 都可以,以下命令与平台无关。
  • Python:建议 3.9 或更高版本,3.7 以上大部分代码也能运行。
  • pandas:建议 1.5 以上或 2.x 版本。版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路和清洗逻辑。
  • IDE:推荐 Jupyter Notebook 或 VS Code,便于逐步观察中间结果。

在终端中安装依赖:

pip install pandas numpy

如果你使用的 Anaconda 环境,pandas 通常已经内置,可以直接跳过这一步。为了不污染全局环境,也可以先创建虚拟环境:

python -m venv airbnb_env source airbnb_env/bin/activate # Windows 下使用 airbnb_env\Scripts\activate pip install pandas numpy

建议的项目目录结构如下:

airbnb_analysis/ ├── data/ │ └── listings.csv ├── notebooks/ │ └── cleaning_shaping.ipynb ├── output/ └── scripts/ └── clean_listings.py

data目录放原始数据,output目录放清洗后的文件,脚本统一放在scripts下。养成数据目录和代码目录分离的习惯,可以避免把原始数据改乱。

3. 数据读取与初步探查

3.1 读取 CSV 文件

先用 pandas 把数据读进来。假设listings.csv位于data目录下:

import pandas as pd DATA_PATH = "data/listings.csv" df = pd.read_csv(DATA_PATH)

这里有一个常用参数:如果你需要预览前几行,可以直接看head();如果文件很大,可以指定nrows=1000先加载前 1000 行做快速验证,避免每次读取全量数据等待时间太长。

df_sample = pd.read_csv(DATA_PATH, nrows=1000)

3.2 查看数据概览

读取之后,先用几个基础命令建立对数据的整体印象。

print("数据形状:", df.shape) print("列名:", df.columns.tolist()) df.head()

一个典型的输出可能是:

数据形状: (28732, 16) 列名: ['id', 'name', 'host_name', 'neighbourhood_group', 'room_type', 'price', 'minimum_nights', 'number_of_reviews', 'last_review', 'availability_365', 'latitude', 'longitude', ...]

shape可以知道有多少行、多少列。从列名能大概看出这份表包含哪些信息。此时建议继续用info()查看每一列的非空数量和数据类型:

df.info()

info()输出里如果某列显示object,通常意味着它被 pandas 当成了文本,下一步要做类型转换。如果某列的非空数量明显小于总行数,说明存在缺失值。

3.3 缺失值与类型概览

快速查看每列缺失值数量:

missing = df.isna().sum() missing_percent = (df.isna().sum() / len(df) * 100).round(2) missing_df = pd.DataFrame({"缺失数量": missing, "缺失占比": missing_percent}) missing_df

输出样式如下:

字段缺失数量缺失占比
host_name120.04%
last_review362012.60%
price00.00%

通过这步,可以快速圈出需要重点处理的列。一般来说,缺失占比小于 1% 的列可以直接填充或删除少量行;缺失占比超过 20% 的列需要认真思考业务含义;如果某列缺失 90% 以上,基本可以考虑整列丢弃。

4. 数据清洗

4.1 删除重复记录

Airbnb 数据在收集或合并过程中,可能出现重复行。先用duplicated()检查:

print("完全重复行数:", df.duplicated().sum())

如果重复行数很多,用drop_duplicates()删除:

df = df.drop_duplicates().copy()

但在真实数据里,“完全重复”其实不算常见,更常见的是部分字段重复,例如同一个id出现了两行,但其中一行的name或价格字段有出入。此时应该根据业务主键去重:

df = df.drop_duplicates(subset=["id"], keep="first").copy()

keep="first"表示保留第一条,keep="last"表示保留最后一条。这里需要说明的是,按id去重会丢失部分信息,所以执行前一定要先确认id是应该唯一的字段,备份原始数据,并记录去重数量以便后续核对。

4.2 处理缺失值

处理缺失值没有“万能药”,关键是理解业务含义。

  • price缺失:严重影响价格分析或建模,建议直接删除对应行,或者用同区域、同房型的中位数填充。
  • host_name缺失:对分析影响较小,可以用"Unknown"填充。
  • last_review缺失:如果对应number_of_reviews为 0,缺失是正常的,表示该房源没有评论,可以保留缺失或填充NaT;如果评论数大于 0 但日期缺失,则建议删除该行或结合其他字段补全。
  • name缺失:通常很少,但缺失时会导致文本分析失败,可以删除或填充"No title"

一个比较稳妥的清洗逻辑:

# 删除关键字段缺失的行 df = df.dropna(subset=["id", "price", "room_type"]).copy() # 分类字段填充 df["host_name"] = df["host_name"].fillna("Unknown") # 日期字段保持缺失,后续用 pd.to_datetime 统一处理 df["last_review"] = pd.to_datetime(df["last_review"], errors="coerce")

以上代码中,dropna(subset=[...])是“有取有舍”,只删除对业务影响最大的脏数据。

4.3 类型转换与价格标准化

Airbnb 数据里最常见的格式问题就是price。原始数据中它可能是"$120.00""$95""$1,200.00"甚至"$0.00",如果不处理就无法做数值统计。先看一下所有价格样本:

print(df["price"].unique()[:20])

如果确认是字符串,清洗思路是去掉$和逗号,再转成数值:

def parse_price(value): if pd.isna(value): return pd.NA s = str(value).replace("$", "").replace(",", "").strip() return pd.to_numeric(s, errors="coerce") df["price"] = df["price"].apply(parse_price)

执行后再次检查:

print(df["price"].dtype) print(df["price"].describe())

如果price已经是float64,说明转换成功。此时可以观察最大最小值,判断是否存在明显异常值。

4.4 异常值处理

价格字段最常见的异常值是小于等于 0 的价格和极端高价格。小于等于 0 的价格通常是数据录入错误或测试房源,可以直接删除。极端高价需要结合业务判断,比如有些豪宅每晚几万美元也是合理数据。

一个实用的做法是:先看分位数,再结合业务阈值处理。

# 删除非正价格 df = df[df["price"] > 0].copy() # 查看 99% 分位数 print(df["price"].quantile(0.99))

如果 99% 分位数是 1000 美元,那超过 5000 美元的数据大概率是有问题的,可以进一步排查。但我不建议直接粗暴剔除,因为 Airbnb 里确实存在高端房源。更推荐的做法是把可疑记录单独保存到output/suspicious_prices.csv,人工确认后再决定是否保留。

异常值处理之后,可以用describe()再次确认分布:

df["price"].describe()

5. 数据整形

清洗完“脏”的部分,接下来把数据结构整理成更便于分析的样子。如果说清洗是“去病”,整形就是“塑形”。

5.1 列名规范化

Airbnb 原始 CSV 的列名有时包含空格或大小写混杂,建议统一成小写蛇形命名,便于后续写代码时不用频繁查列名。

df.columns = [col.strip().lower().replace(" ", "_") for col in df.columns]

执行后,Number Of Reviews会被改成number_of_reviews。这里要注意,原始列名可能包含其他特殊字符,比如连字符或括号,建议按实际输出再调整一次替换规则。

5.2 文本字段清理

room_type这类分类字段常见问题是大小写和前后空格不一致。例如:

  • Entire home/apt
  • Private room
  • private room
  • Hotel room

如果不统一,value_counts()会把同一个类别拆成多个。清理方法:

df["room_type"] = df["room_type"].str.lower().str.strip()

处理后再看类别分布:

print(df["room_type"].value_counts())

可以看到所有类别被压缩成少数几个标准值。同样的方法也可以用于nameneighbourhood_group等文本列。

5.3 日期字段整形

日期字段的目标是统一成datetime类型,这样后续才能计算“距今天数”“按月份聚合”等特征。

date_columns = ["host_since", "last_review"] for col in date_columns: if col in df.columns: df[col] = pd.to_datetime(df[col], errors="coerce")

errors="coerce"的作用是:无法解析的日期自动变成NaT,不会让程序中断。不过也要注意,pd.to_datetime在遇到不同日期格式混用时会弹出警告。如果 pandas 版本支持format="mixed",可以显式指定;如果版本较旧,建议先查看不合规样本再单独处理。

5.4 派生特征

整形阶段很适合做特征工程。基于已有字段,可以顺手生成几个对分析和建模都有用的新特征:

from datetime import datetime # 计算房源在平台上的天数 if "host_since" in df.columns: df["listing_age_days"] = (datetime.now() - df["host_since"]).dt.days # 是否拥有评论 df["has_reviews"] = df["number_of_reviews"].fillna(0).map(lambda x: 1 if x > 0 else 0) # 价格区间分箱,方便做分布分析 price_bins = [0, 100, 200, 500, 10000] price_labels = ["0-100", "100-200", "200-500", "500+"] df["price_range"] = pd.cut(df["price"], bins=price_bins, labels=price_labels, right=False)

需要提醒的是,listing_age_days的计算结果会随运行时间变化。如果希望分析可复现,更推荐把“数据截止日期”作为参数传进来,比如固定使用asof_date = datetime(2024, 1, 1),而不是用datetime.now()。这一点在做时间敏感性分析时尤其重要。

5.5 区域坐标过滤与字段筛选

Airbnb 数据里,经纬度字段偶尔会有明显错误,比如经度或纬度越界。可以用一个简单的布尔条件过滤:

# 示例:仅保留特定城市的合理坐标范围,具体阈值需根据城市调整 valid_coords = ( df["latitude"].between(40.5, 41.0) & df["longitude"].between(-74.3, -73.7) ) df = df[valid_coords].copy()

此外,整形阶段也是决定“最终保留哪些列”的时机。如果目标是价格预测,可能只需要room_typepriceavailability_365neighbourhood_group、派生特征等;如果目标是文本分析,那name就是核心列。用下面的方式保留指定字段:

keep_columns = [ "id", "name", "host_name", "neighbourhood_group", "room_type", "price", "price_range", "minimum_nights", "number_of_reviews", "has_reviews", "last_review", "availability_365", "listing_age_days" ] df_clean = df[keep_columns].copy()

6. 完整实战案例

前面几节把流程拆成了一个个步骤,这一节用一个模拟数据集把整套流程串起来。示例数据是构造的,字段结构尽量对齐真实 Airbnb Listings,方便你替换成自己的 CSV。

6.1 构造一份模拟数据

import pandas as pd raw_rows = [ {"id": 1, "name": "Cozy Room in Brooklyn", "host_name": "Alice", "neighbourhood_group": "Brooklyn", "room_type": "Entire home/apt", "price": "$125.00", "minimum_nights": 3, "number_of_reviews": 45, "last_review": "2023-06-15", "availability_365": 280, "latitude": 40.678, "longitude": -73.944}, {"id": 2, "name": "Manhattan Studio", "host_name": "Bob", "neighbourhood_group": "Manhattan", "room_type": "Private room", "price": "$95", "minimum_nights": 1, "number_of_reviews": "5", "last_review": "2022/11/03", "availability_365": 0, "latitude": 40.753, "longitude": -73.983}, {"id": 3, "name": "Shared Room Near Park", "host_name": None, "neighbourhood_group": "Queens", "room_type": " shared room ", "price": "$1,200.00", "minimum_nights": 7, "number_of_reviews": 100, "last_review": "2023-02-10", "availability_365": 365, "latitude": 40.714, "longitude": -73.831}, {"id": 4, "name": "Luxury Apartment", "host_name": "Cathy", "neighbourhood_group": "Manhattan", "room_type": "Entire home/apt", "price": "$0.00", "minimum_nights": 2, "number_of_reviews": 12, "last_review": None, "availability_365": 60, "latitude": 40.760, "longitude": -73.984}, {"id": 5, "name": "Budget Room", "host_name": "David", "neighbourhood_group": "Bronx", "room_type": "Private Room", "price": "$99.00", "minimum_nights": 1, "number_of_reviews": 0, "last_review": None, "availability_365": 30, "latitude": 40.845, "longitude": -73.886}, {"id": 1, "name": "Cozy Room in Brooklyn", "host_name": "Alice", "neighbourhood_group": "Brooklyn", "room_type": "Entire home/apt", "price": "$125.00", "minimum_nights": 3, "number_of_reviews": 45, "last_review": "2023-06-15", "availability_365": 280, "latitude": 40.678, "longitude": -73.944}, {"id": 6, "name": "Invalid Coord House", "host_name": "Eve", "neighbourhood_group": "Manhattan", "room_type": "Hotel room", "price": "$300.00", "minimum_nights": 2, "number_of_reviews": 20, "last_review": "2023-01-01", "availability_365": 10, "latitude": 100.00, "longitude": -73.984}, ] df_raw = pd.DataFrame(raw_rows) df_raw.to_csv("listings_demo.csv", index=False) print(df_raw)

上面的模拟数据涵盖了重复行、缺失值、价格格式混乱、日期格式混杂、经纬度异常等常见脏数据问题。

6.2 编写完整清洗整形函数

下面把前面的步骤封装成一个函数,后续处理任意 CSV 时只需把路径传入:

from datetime import datetime CITY_LAT_RANGE = (40.5, 41.0) CITY_LON_RANGE = (-74.3, -73.7) def parse_price(value): if pd.isna(value): return pd.NA s = str(value).replace("$", "").replace(",", "").strip() return pd.to_numeric(s, errors="coerce") def clean_and_shape_listings(df, asof_date=None): # 避免修改原始数据 data = df.copy() # 1. 列名规范化 data.columns = [col.strip().lower().replace(" ", "_") for col in data.columns] # 2. 按 id 去重 before = len(data) data = data.drop_duplicates(subset=["id"], keep="first") print(f"去重前行数: {before}, 去重后行数: {len(data)}") # 3. 英文列名中如果存在,删除关键字段缺失的行 data = data.dropna(subset=["id", "price", "room_type"]).copy() # 4. 价格解析 data["price"] = data["price"].apply(parse_price) # 5. 删除非正价格 data = data[data["price"] > 0].copy() # 6. 文本字段规范化 data["room_type"] = data["room_type"].astype(str).str.lower().str.strip() data["name"] = data["name"].fillna("No title").astype(str).str.strip() data["host_name"] = data["host_name"].fillna("Unknown").astype(str).str.strip() # 7. 日期全量化 for col in ["host_since", "last_review"]: if col in data.columns: data[col] = pd.to_datetime(data[col], errors="coerce") # 8. 数值字段统一 for col in ["minimum_nights", "number_of_reviews", "availability_365"]: if col in data.columns: data[col] = pd.to_numeric(data[col], errors="coerce").fillna(0) # 9. 删除明显越界的坐标 data = data[data["latitude"].between(*CITY_LAT_RANGE)] data = data[data["longitude"].between(*CITY_LON_RANGE)] # 10. 派生特征 if asof_date is None: asof_date = datetime.now() data["listing_age_days"] = (pd.Timestamp(asof_date) - data["host_since"]).dt.days data["has_reviews"] = (data["number_of_reviews"] > 0).astype(int) data["price_range"] = pd.cut( data["price"], bins=[0, 100, 200, 500, 100000], labels=["0-100", "100-200", "200-500", "500+"], right=False ) # 11. 保留目标字段 keep_columns = [ "id", "name", "host_name", "neighbourhood_group", "room_type", "price", "price_range", "minimum_nights", "number_of_reviews", "has_reviews", "last_review", "availability_365", "listing_age_days" ] keep_columns = [col for col in keep_columns if col in data.columns] data = data[keep_columns].copy() return data

使用方式:

df_clean = clean_and_shape_listings(df_raw, asof_date=datetime(2024, 1, 1)) print(df_clean)

6.3 运行与验证

运行代码后,预期输出大致是:

去重前行数: 7, 去重后行数: 6 清洗后行数: 4

最终清洗后的数据集应该只剩 4 行,因为重复行、缺失价格的行、非正价格、错误坐标分别被删除。price变成float64last_review变成datetime64[ns]room_type变成统一的小写格式。

6.4 导出结果

清洗完成后导出到output目录:

import os os.makedirs("output", exist_ok=True) df_clean.to_csv("output/listings_clean.csv", index=False)

也可以顺手保存一个带data_version的日期后缀文件,方便区分版本:

df_clean.to_csv("output/listings_clean_20240101.csv", index=False)

这里使用os.makedirs("output", exist_ok=True)能保证目录不存在时自动创建。后面常见问题里会提到,不做这一步就可能碰到目录创建失败。

7. 常见问题与排查思路

处理 Airbnb Listings 数据时,下面几个问题出现频率很高:

问题现象常见原因解决思路
price无法转成数字字符串中存在$、逗号、空格或其他符号str.replace()清理,再用pd.to_numeric(errors="coerce")
日期解析报错或出现大量NaT日期格式混杂,如2023-06-152022/11/03共存pd.to_datetime(..., errors="coerce"),然后单独排查转为NaT的样本
去重后数据量骤减误把不该当作主键的列放入subset先确认业务主键,比如id是否确实唯一
room_type类别非常多大小写、空格、拼写不统一统一转小写、去空格,再用value_counts()检查
导出时目录不存在使用了相对路径且目标目录未创建使用os.makedirs("output", exist_ok=True)Path.mkdir(parents=True, exist_ok=True)

下面单独展开两个比较典型的问题。

7.1 价格字段解析失败

如果parse_price之后出现大量NaN,先检查原始字符串到底长什么样:

bad_price = df[df["price"].isna()] print(bad_price["price"].head(20))

常见情况是价格字段里混入了不规则字符,例如"价格面议""$1,200.00/月"等。对于这类数据,建议保留原始列,并把解析结果单独存为新列,避免把原始信息丢失。

7.2 重复值删除过度

drop_duplicates(subset=["id"])如果删掉的行数量异常,可能是同一个id下实际有不同的房源信息,例如共享房源中不同房型共用同一id。遇到这种情况,可以改为按id + room_type + price去重,或者在前一步先通过sort_values()把质量更高的行排到前面,再去重。

7.3 创建输出目录时报 cannot mkdir structure meed cleaning

如果你在自动化处理脚本里遇到类似cannot mkdir structure meed cleaning的提示,不要只盯着目录名本身。这个报错的意思是:脚本尝试创建目录,但目录结构没有准备好,需要先清理或调整。常见的触发原因有三个。

第一,目标路径上已经存在一个同名普通文件。例如你有一个文件叫output,脚本又想创建同名的output/目录,系统就会拒绝。解决方法是先检查并重命名或删除冲突文件。

import os from pathlib import Path output_dir = Path("output") if output_dir.exists() and not output_dir.is_dir(): output_dir.rename("output_conflict_backup") output_dir.mkdir(parents=True, exist_ok=True)

第二,父目录不存在,且脚本没有递归创建目录。在 Python 中,只用os.mkdir("data/processed")时,如果dataprocessed都不存在,会直接报错。推荐使用:

from pathlib import Path Path("output/listings_clean").mkdir(parents=True, exist_ok=True)

第三,目录里残留旧的中间文件导致脚本状态混乱。这里的structure meed cleaning其实是在提醒你:当前目录结构已经和脚本预期不一致,建议把旧的临时目录清理掉,重新初始化一个干净的输出目录。

在数据工程脚本中,建议在脚本开头固定一段“初始化目录”的逻辑,把错误消灭在第一步:

import shutil from pathlib import Path output_dir = Path("output") if output_dir.exists(): shutil.rmtree(output_dir) output_dir.mkdir(parents=True, exist_ok=True)

这个方案适合完全由脚本生成的临时目录,但如果目录里有重要手工文件,删除前一定要先备份或加确认条件。

8. 最佳实践与工程建议

如果只是做一次性的探索性分析,前面几节已经够用。但如果这个清洗流程要反复使用,或者要交给团队其他成员使用,下面这些工程习惯值得注意。

8.1 保留原始数据副本

清洗前先备份原始文件。最简单的方法是把原始 CSV 复制为带raw后缀的文件,或者把原始DataFrame保留在内存中不覆盖。数据清洗是不可逆操作,一旦在原文件上直接修改,后面想恢复就非常麻烦。

df_raw_backup = df_raw.copy()

8.2 把清洗流程封装成函数

不要把所有步骤一股脑写在 Jupyter Notebook 的某个单元格里,建议封装成clean_and_shape_listings()这样的函数。这样做的优势在于:可以批量处理多个城市的 Airbnb 数据;可以接入测试框架;可以用相同的清洗规则处理训练集和预测集,避免数据泄漏或规则不一致。

8.3 记录清洗日志

在函数里用print()或日志库记录每一步删除的行数和关键统计量。比如:

print(f"删除缺失价格行: {len(df) - len(df.dropna(subset=['price']))}")

清洗结束后,可以自动生成一份包含原始数据行数、清洗后行数、删除原因统计的日志文件,便于复盘。

8.4 控制输出字段范围

如果清洗后的数据要共享给同事或用于外部展示,建议移除包含个人隐私的字段。Airbnb Listings 原始数据中通常包含房东姓名、精确经纬度等敏感信息,不必要的字段不要导出,分析时只保留分析需要的列即可,这也是数据处理中的最小权限原则。

8.5 使用配置文件管理路径

如果脚本要在不同机器上运行,不要把文件路径硬编码在每个脚本里。可以写一个简单的配置文件config.yamlconfig.json,统一管理数据路径、城市经纬度范围、价格阈值等参数。这样换城市或换服务器时,只需要修改配置,不用动代码。

{ "raw_data_path": "data/listings.csv", "output_dir": "output", "city_lat_range": [40.5, 41.0], "city_lon_range": [-74.3, -73.7] }

8.6 大数据量下的读取策略

如果listings.csv有几个 GB,一次读入内存会比较吃力。此时可以用chunksize分块读取,先清洗每一块,再把清洗后的结果累计到最终表中:

chunks = pd.read_csv("data/listings.csv", chunksize=100000) df_clean_list = [] for chunk in chunks: df_clean_list.append(clean_and_shape_listings(chunk)) df_all = pd.concat(df_clean_list, ignore_index=True)

不过分块处理时要注意,重复值去重和全局统计量(比如分位数、缺失率)不能简单地按块计算,需要先在全局层面确认规则,然后再分块清洗。

8.7 用 pandas 内置方法减少代码量

很多清洗操作其实有更简洁的写法。例如判空可以用df["price"].str.contains("\\$"),数值规范化可以用df["price"].str.replace("[$,]", "", regex=True)。但代码简洁的前提是可读性,建议在团队协作时优先选择注释完整、逻辑清晰的写法。

结语

Airbnb Listings 数据的清洗和整形并不难,难的是把每一步想清楚:哪个字段是业务主键,缺失值应该删除还是填充,异常价格是该剔除还是单独保留。建议你拿到一份新数据后,不要急着写模型,先花一小时把本文的流程完整跑一遍,做出第一版干净数据表。后续做可视化、特征工程和模型训练时,你会明显感觉到数据顺手很多。

最后说一个很实用的习惯:每次跑清洗脚本前,先复制一份原始 CSV,命名为listings_raw_backup.csv。数据清洗是不可逆操作,很多翻车案例都是因为在原文件上直接修改后再也恢复不了。把这条记在项目的 README 里,能省掉不少麻烦。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 20:36:01

奇安信服务端应用开发面试复盘:四方向底层逻辑与核心能力解析

2020年4月8日,我参加了奇安信服务端开发工程师-应用开发方向的技术面试。这个岗位有意思的地方在于,它不是一个笼统的“后端开发”,而是明确拆成了四个方向,面试前会让你选,或者说面试官会根据你的简历背景把你往某个方…

作者头像 李华
网站建设 2026/9/1 20:34:44

AI原生开发推理成本控制:从部署到调优的实战指南

AI原生开发这两年讨论很多,但真正把项目从 Demo 推到线上的人会发现,第一个卡住的地方往往不是模型能力,而是推理成本。这里说的推理成本不只是 API 账单,也包括本地部署时的显存、内存、GPU 占用、任务排队时间,以及批…

作者头像 李华
网站建设 2026/9/1 20:34:40

告别百万域名库:用eBPF动态DPI让软路由流量识别更高效

最近我帮朋友整理一台软路由,现象很有意思:平时跑满千兆都没压力的设备,最近网页频繁打不开,CPU 动不动就飙到 90%,内存也在持续上涨。打开进程一看,负责流量识别的服务正在后台一遍一遍地遍历一张百万行级…

作者头像 李华
网站建设 2026/9/1 20:32:18

GSDML文件全解析:从文件名到PROFINET设备组态实战

简介:本资源为PepperlFuchs公司ICE1系列工业传感器/执行器的标准化设备描述文件包,面向自动化系统集成工程师、PLC编程人员及现场调试技术人员,用于解决PROFIBUS/PROFINET等现场总线系统中设备选型、组态导入与通信参数配置等核心问题。压缩包…

作者头像 李华
网站建设 2026/9/1 20:28:10

无刷电机短路炸机根因解析:从原理到排查预防的完整指南

飞了很久的无人机,某天起飞时推油没反应,或者半空中突然“啪”一声,紧接着一股焦糊味,落地一看电机绕线已经发黑,电调也烧了。更头疼的是,明明没有炸过机、没有碰撞,电机却“莫名其妙”短路了。…

作者头像 李华
网站建设 2026/9/1 20:27:20

Keil MDK中.s启动文件详解:从复位到main的执行流程

搞单片机如果一直停留在“点灯、按键、串口打印”这种应用层,早晚会在硬件不稳定、程序跳飞、中断进不去这些问题上摔跟头。而这些问题,十有八九要回溯到芯片上电后的第一个动作——启动文件。在 Keil 的 MDK 工程里,启动文件就是那个后缀为 …

作者头像 李华