2026年4月7日 研究日志¶

今日也在数据清洗中,对的,作为一个数据科学家,实际上数据清理占了很大量的工作时间,说真的,对我来说,时间分配上读文献做文书工作占4成,数据清理占4成,实际顶多有2成时间是在编写代码进行数据分析。数据算不上保密,因为今天用的是公开的GEO数据集GSE135133,这是个单细胞测序数据集,准确说是单核测序数据集。不过这个大型数据集我可放不上来,今天就展示一下我的清理pipeline吧。注意,这仅仅是一个自动化的,修复通用问题的pipeline,而现实中每个数据集都有可能有自己独特的错误,不管有多么自动化的工具,都一定自己留心,对自己的数据负责,才能对自己的结论负责。

flowchart TD

    A[raw_df
原始数据框] --> B[clean_colnames
统一列名] B --> C[drop_empty
删除空行空列] C --> D[fix_types
修复列类型] D --> E[normalize_strings
标准化字符串] E --> F[remove_duplicates
去除重复行] F --> G[impute_missing
填补缺失值] G --> H[extra_steps
可选额外步骤] H --> I[df_clean
清洗后的数据框]
In [ ]:
library(janitor)
library(tidyr)
library(dplyr)
library(stringr)

# ================================
# 1. 清洗函数
# ================================
clean_colnames <- function(df) {
  names(df) <- janitor::make_clean_names(names(df))
  df
}

drop_empty <- function(df) {
  df %>% janitor::remove_empty(c("rows", "cols"))
}

fix_types <- function(df) {
  df %>% mutate(across(where(is.character), ~ trimws(.x)))
}

normalize_strings <- function(df) {
  df %>% mutate(across(where(is.character), ~ tolower(.x)))
}

remove_duplicates <- function(df) {
  df %>% distinct()
}

impute_missing <- function(df) {
  df %>% mutate(across(where(is.numeric), ~ ifelse(is.na(.x), median(.x, na.rm = TRUE), .x)))
}

# ================================


# ================================
# 2. 日志函数
# ================================
log_step <- function(msg) {
  cat(sprintf("[INFO] %s\n", msg))
}


# ================================
# 3. 主 Pipeline
# ================================
clean_pipeline <- function(df,
                           do_colnames   = TRUE,
                           do_drop_empty = TRUE,
                           do_fix_types  = TRUE,
                           do_normalize  = TRUE,
                           do_dedup      = TRUE,
                           do_impute     = TRUE,
                           extra_steps   = list()) {
  
  # 内部步骤列表
  steps <- list()
  
  if (do_colnames)   steps <- append(steps, list(clean_colnames))
  if (do_drop_empty) steps <- append(steps, list(drop_empty))
  if (do_fix_types)  steps <- append(steps, list(fix_types))
  if (do_normalize)  steps <- append(steps, list(normalize_strings))
  if (do_dedup)      steps <- append(steps, list(remove_duplicates))
  if (do_impute)     steps <- append(steps, list(impute_missing))
  
  # 添加额外步骤
  steps <- c(steps, extra_steps)
  
  # 执行 Pipeline
  for (f in steps) {
    log_step(paste("Running step:", deparse(substitute(f))))
    df <- f(df)
  }
  
  log_step("Pipeline completed.")
  df
}


# ================================
# 4. 使用
# ================================
# df_clean <- clean_pipeline(raw_df)

# 带额外步骤
# df_clean <- clean_pipeline(raw_df, extra_steps = list(
#   function(x) mutate(x, new_col = row_number())
# ))

今天搞得头晕目眩的,好累,具体解释找机会吧,今天先结束工作了,各位,下个工作日见!