示例成品 看全文 ↓

看数据缺失是偶然还是有原因

直接填补空值可能掩盖采集故障或人群选择偏差,最终结论会失真。

免费看示例成品:报告 · 代码写代码做网站 ›

有人这样说「这波用户留存数据缺得离谱,发群问了没人知道原因,得查清楚。」

能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。

模板:已按内容配好换一套

标题、表头的配色和字体

已替你选好想改就点一下

只有你知道的不填就先空着,成品里留【待填】

还想怎么改可以不选

开工前请确认

高级选项一般不用动 · 这件事通常 1 分钟左右做好

示例成品全文按上面这组示例选项真跑出来的

当前没看到你发的 CSV 文件本体和“文档第7页”原文,所以不能直接跑出“每条缺记录”的清单。下面先把能做的做掉:给出缺失记录标注口径、缺失率公式、共现指标公式,以及一个可以直接跑的 Python 脚本。你补数据后,按这个跑就能出结果。

一、缺失怎么算

重点字段:时间戳、用户ID。

一条记录只要出现以下任一情况,就标为“缺记录”:

  • 时间戳为空 / 解析失败
  • 用户ID为空
  • 其他字段为空或常见空值占位符:NULL、null、None、NaN、NA、空字符串、\N 等

输出文件会包含:

  • 原始行号
  • 时间戳
  • 用户ID
  • 缺了哪些字段

二、缺失率公式

设总记录数为 \(N\)。

1. 总体缺失率

\[
\text{字段缺失率} = \frac{\text{该字段缺失条数}}{N}
\]

2. 按天缺失率

\[
\text{某天字段缺失率} = \frac{\text{某天该字段缺失条数}}{\text{某天总记录数}}
\]

3. 按小时缺失率

\[
\text{某小时字段缺失率} = \frac{\text{某小时该字段缺失条数}}{\text{某小时总记录数}}
\]

时间戳本身缺失的记录无法归入某天/某小时,会单独列出,不参与按天/按小时分桶。

三、共现指标公式

共现热力图主要看“一个字段缺的时候,另一个字段是不是也一起缺”。

1. 共现缺失率

\[
\text{JointMiss}(A,B) = \frac{A \text{ 和 } B \text{ 同时缺失的条数}}{N}
\]

2. 条件缺失率

\[
\text{CondMiss}(B|A) = \frac{A \text{ 和 } B \text{ 同时缺失的条数}}{A \text{ 缺失的条数}}
\]

含义:A字段缺的时候,B字段也缺的比例。

3. Jaccard 相似度

\[
J(A,B) = \frac{A \text{ 和 } B \text{ 同时缺失的条数}}{A \text{ 或 } B \text{ 至少一个缺失的条数}}
\]

热力图建议优先看 Jaccard,因为它能避免“某个字段本身缺失太多,导致看起来跟谁共现都很高”的误导。

四、直接可用脚本

把 CSV 文件放在同目录下,按实际列名改 TS_COL、UID_COL。

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# ===== 配置 =====
FILE = "用户留存表v3.csv"
TS_COL = "时间戳"        # 按实际列名改
UID_COL = "用户ID"       # 按实际列名改
OUT_MISSING_RECORDS = "缺记录明细.csv"
OUT_GAP_RECORDS = "疑似缺失时段.csv"
EXPECTED_FREQ = "1D"      # 如果数据应按天同步;若按小时改 "1H"
GAP_THRESHOLD = pd.Timedelta(EXPECTED_FREQ) * 1.5

# ===== 读取 =====
df = pd.read_csv(FILE)
df[TS_COL] = pd.to_datetime(df[TS_COL], errors="coerce")

# ===== 缺失值定义 =====
MISS_VALUES = {'', 'NULL', 'null', 'None', 'none', '\\N', 'nan', 'NaN', 'NA', 'N/A', 'missing', '缺失'}

def is_missing(x):
    if pd.isna(x):
        return True
    try:
        return str(x).strip() in MISS_VALUES
    except Exception:
        return False

# ===== 1. 每条缺记录标注 =====
original_cols = list(df.columns)
all_check_cols = [TS_COL, UID_COL] + [c for c in original_cols if c not in [TS_COL, UID_COL]]

df["缺字段"] = df[all_check_cols].apply(
    lambda row: [c for c in row.index if is_missing(row[c])], axis=1
)
df["是否缺记录"] = df["缺字段"].apply(len) > 0

miss_records = df[df["是否缺记录"]].copy()
miss_records[["行号", TS_COL, UID_COL, "缺字段"]].to_csv(
    OUT_MISSING_RECORDS, index=True, encoding="utf-8-sig"
)

print("缺记录条数:", len(miss_records))
print("缺记录明细已保存到:", OUT_MISSING_RECORDS)

# ===== 2. 缺失率 =====
miss_total = pd.DataFrame({
    "字段": all_check_cols,
    "缺失条数": [df[c].apply(is_missing).sum() for c in all_check_cols],
})
miss_total["总条数"] = len(df)
miss_total["总体缺失率"] = miss_total["缺失条数"] / miss_total["总条数"]
print("\n总体缺失率:")
print(miss_total)

# 时间可解析的记录才做按天/按小时分桶
df_time = df.dropna(subset=[TS_COL]).copy()
df_time["日期"] = df_time[TS_COL].dt.date
df_time["小时"] = df_time[TS_COL].dt.hour

# 重点字段缺失布尔列
for c in [UID_COL]:
    df_time[f"{c}_缺"] = df_time[c].apply(is_missing)

# 按天缺失率
daily_miss = df_time.groupby("日期")[[f"{UID_COL}_缺"]].mean().rename(columns={f"{UID_COL}_缺": "用户ID缺失率"})
daily_count = df_time.groupby("日期").size().rename("记录数")
daily_miss = daily_miss.join(daily_count)

# 按小时缺失率
hourly_miss = df_time.groupby("小时")[[f"{UID_COL}_缺"]].mean().rename(columns={f"{UID_COL}_缺": "用户ID缺失率"})
hourly_count = df_time.groupby("小时").size().rename("记录数")
hourly_miss = hourly_miss.join(hourly_count)

print("\n按天缺失率:")
print(daily_miss)
print("\n按小时缺失率:")
print(hourly_miss)

# ===== 3. 共现热力图 =====
miss_matrix = pd.DataFrame({c: df[c].apply(is_missing).astype(int) for c in all_check_cols})

# Jacc

别人还这样问

同类还能办

全部 ›
分库分表方案怎么设计免费看示例 ›发票信息批量录入开票系统脚本免费看示例 ›数据库选型怎么选免费看示例 ›哪些缓存能继续用免费看示例 ›整理制动测试的数据免费看示例 ›抽样检查标注准不准免费看示例 ›

同样出写代码做网站的

首页