4.分组内top前几

核心思路

  • 需求常见词:【每组xxx的第一个yyyzzz】【每组xxx的最后一个】
    【每组xxx的前n个】【每组最xx的前n个】
  • 公式:row_number() over(partition by 分组字段 order by 排序字段) as rn,再筛选rn<=N名

例题一:跨越物流

create table emp(empno string ,ename string,hiredate string,sal int ,deptno string);
insert overwrite table emp values
('7521', 'WARD', '1981-2-22', 1250, 30),
('7566', 'JONES', '1981-4-2', 2975, 20),
('7876', 'ADAMS', '1987-7-13', 1100, 20),
('7369', 'SMITH', '1980-12-17', 800, 20),
('7934', 'MILLER', '1982-1-23', 1300, 10),
('7844', 'TURNER', '1981-9-8', 1500, 30),
('7782', 'CLARK', '1981-6-9', 2450, 10),
('7839', 'KING', '1981-11-17', 5000, 10),
('7902', 'FORD', '1981-12-3', 3000, 20),
('7499', 'ALLEN', '1981-2-20', 1600, 30),
('7654', 'MARTIN', '1981-9-28', 1250, 30),
('7900', 'JAMES', '1981-12-3', 950, 30),
('7788', 'SCOTT', '1987-7-13', 3000, 20),
('7698', 'BLAKE', '1981-5-1', 2850, 30);

select * from emp;

在这里插入图片描述

题目描述

求出每个部门工资最高的前三名员工,并计算这些员工每个员工的工资占所属部门总工资的百分比。

分析:最终结果中需要包含员工信息,然后还需要包含三个新增的列(伪列),排名、部门总工资、员工占总工资的百分比

with t1 as (
    select
        *,
        row_number() over (partition by deptno order by sal desc) as rn,
        -- sum窗口函数没有order by时,即分组后的指定列值的总和,有order by时,则是指定列值的前缀累加和
        sum(sal) over (partition by deptno) as sum_sal
    from emp)

select
    *,
    round(sal / sum_sal,2)  as rate
from t1
where rn <= 3;

在这里插入图片描述

例题二:小米电商

订单表名字:t_order.
字段:user_id, order_id, ctime(10位时间戳),city id,sale_num,sku_id(商品)

问题:20201201至今每日订单量top10的城市及其订单量(订单量对order id去重)`

create table t_order (user_id string, 
                      order_id string, 
                      ctime string, 
                      city_id string, 
                      sale_num int , 
                      sku_id string) ;

第一步:对数据做相关预处理,如时间戳转为日期,过滤出指定时间范围的订单,对订单id去重统计订单量
with t1 as (select to_date(ctime) cdate, city_id, count(distinct order_id) cnt
            from t_order
            where to_date(ctime) >= '2020-12-01'
              and to_date(ctime) <= `current_date`()
            group by to_date(ctime), city_id),

第二步:对日期分组,对销售量排序
     t2 as (select *, row_number() over (partition by cdate order by cnt desc) rn from t1)

第三步:过滤出top10,保留所需结果字段
select cdate, city_id, cnt
from t2
where rn <= 10;
<think>我们遇到了一个错误:`Error in(1- h) *qs[i]:二进列运算符中有非数值参数`。这个错误通常发生在进行数值计算时,参数中包含非数值类型(如字符型或逻辑型)。结合我们的代码,问题可能出现在以下个方面:1.**生存对象(`surv_obj`)的创建**:`Surv`对象要求时间和状态都是数值型。如果状态列不是数值型(例如是字符型或因子型),则会导致错误。 2. **分组(`group_sub`)**:分组应该是字符型或因子型,但我们的代码中已经用字符串("High"和"Low")创建,所以应该没有问题。但是,如果分组中存在非预期的缺失值或类型问题,也可能导致错误。 3. **基因表达(`expr`)**:在计算分位数和分组时,要求基因表达是数值型。如果基因表达列不是数值型,则会导致分组错误。另外,错误信息中提到错误发生在第18个基因上,说明17个基因可能运行正常,而第18个基因的数据可能存在问题(如存在非数值、缺失值过多等)。 ###解决方案步骤####1.检查数据类型首先,我们需要确保数据框`df`中的列都是正确的类型: -生存时间(`time`)和状态(`status`)必须是数值型。-所有基因列必须是数值型。 我们可以使用以下代码检查: ```r#检查时间和状态列str(df$time)str(df$status) #随机检查个基因列str(df[[gene_columns[1]]])str(df[[gene_columns[18]]])#因为错误发生在第18个基因,特别检查这个```如果发现非数值型,需要进行转换:```r#转换时间列和状态列为数值型df$time<- as.numeric(df$time)df$status <-as.numeric(df$status) #转换基因列为数值型for(genein gene_columns) {df[[gene]]<- as.numeric(df[[gene]])} ```####2.处理缺失值在创建分组时,我们使用了`na.rm=TRUE`来忽略缺失值计算分位数,但分组后我们过滤了中间50%的样本(即只保留高表达和低表达组)。然而,如果某个基因的表达存在大缺失值,可能会导致分组后样本过少,进而导致生存分析无法进行(比如分组后某一组没有样本)。我们可以在函数中加入检查,确保分组后每组都有样本: ```ranalyze_gene<- function(gene_name){expr <-df[[gene_name]]quantiles<- quantile(expr,probs= c(0.25,0.75),na.rm= TRUE)gene_group <-ifelse(expr<= quantiles[1],"Low",ifelse(expr>= quantiles[2],"High", NA))valid_idx <-!is.na(gene_group)surv_sub<- surv_obj[valid_idx]group_sub<- gene_group[valid_idx]#检查分组后是否有至少两组且每组至少有一个事件if (length(unique(group_sub))<2){warning(paste("Gene",gene_name, "has onlyone groupafter splitting. Skipping."))return(data.frame(gene =gene_name, p_value =NA))}surv_diff<- survdiff(surv_sub ~group_sub)pval<-1 -pchisq(surv_diff$chisq, df=1)return(data.frame(gene =gene_name, p_value =pval)) }``` ####3.确保生存对象正确创建生存对象`surv_obj`应该是一个`Surv`对象,由时间和状态构成。我们假设在函数外部已经创建好,并且与`df`的行一一对应。注意,`surv_obj`应该是一个,长度与`df`的行数相同。在函数内部,我们使用`surv_sub<- surv_obj[valid_idx]`来获取子集,所以需要确保`surv_obj`的创建是正确的。例如: ```rsurv_obj <-Surv(time= df$time, event= df$status) ```####4.调试具体基因由于错误发生在第18个基因,我们可以单独运行这个基因的代码来检查:```rgene_name <-gene_columns[18]expr <-df[[gene_name]] quantiles <-quantile(expr, probs =c(0.25,0.75), na.rm =TRUE)gene_group<- ifelse(expr <=quantiles[1], "Low",ifelse(expr >=quantiles[2], "High",NA))valid_idx<- !is.na(gene_group)surv_sub <-surv_obj[valid_idx]group_sub<- gene_group[valid_idx] #检查分组后的数据table(group_sub)#查看分组情况str(surv_sub)#查看生存对象子集str(group_sub)#查看分组#尝试运行生存分析surv_diff<- survdiff(surv_sub ~group_sub) ```###修改后的完整代码```rlibrary(survival) library(purrr)#假设df包含时间、状态和多个基因列#创建生存对象(在整个分析创建一次) surv_obj<- Surv(time =df$time,event =df$status)#确保所有基因列为数值型gene_columns <-setdiff(colnames(df),c("time","status")) df[gene_columns]<- lapply(df[gene_columns],as.numeric)#修改后的函数analyze_gene<- function(gene_name){expr <-df[[gene_name]]quantiles<- quantile(expr,probs= c(0.25,0.75),na.rm= TRUE)#创建分组(高表达:>=75%分位数;低表达:<=25%分位数)gene_group <-ifelse(expr<= quantiles[1],"Low",ifelse(expr>= quantiles[2],"High", NA))valid_idx <-!is.na(gene_group)surv_sub<- surv_obj[valid_idx]group_sub<- gene_group[valid_idx]#检查分组后是否有两个组且每组有样本if(length(unique(group_sub)) <2) {return(data.frame(gene= gene_name,p_value= NA))}#执行log-rank检验surv_diff <-survdiff(surv_sub~ group_sub)pval <-1- pchisq(surv_diff$chisq,df =1)return(data.frame(gene= gene_name,p_value= pval))} #应用函数results <-map_dfr(gene_columns,analyze_gene)``` ###注意事项1. **分位数计算**:使用`quantile`函数计算25%和75%分位数,注意`na.rm=TRUE`。2.**分组**:将低于等于25%分位数的样本定义为“低表达”,高于等于75%分位数的样本定义为“高表达”,中间部分设为`NA`,然后过滤掉。3.**生存分析**:使用`survdiff`进行log-rank检验,然后从结果中提取卡方统计并计算p值。###相关问题1.生存分析中除了log-rank检验,还有哪些常用的方法? 2.如何绘制生存曲线来展示高表达组和低表达组的生存差异? 3.在生存分析中,如何处理多个基因的p值校正问题?4.如果基因表达不是正态分布,对生存分析的结果会有影响吗?</think>### 在R中按基因表达25%和75%分位数分组进行生存分析的正确方法 您在R中根据基因表达的25%和75%分位数分组进行生存分析时遇到的错误,通常是由于数据类型不匹配或生存对象创建不当导致的。以下是完整解决方案: #### 正确分组和生存分析代码 ```r # 加载必要包 library(survival) library(dplyr) # 假设df包含时间、状态和基因表达列 # 创建分组函数 analyze_gene <- function(gene_name) { # 获取基因表达数据 expr <- df[[gene_name]] # 计算25%和75%分位数 quantiles <- quantile(expr, probs = c(0.25, 0.75), na.rm = TRUE) # 创建分组(高表达组:≥75%分位数;低表达组:≤25%分位数) gene_group <- case_when( expr >= quantiles[2] ~ "High", expr <= quantiles[1] ~ "Low", TRUE ~ NA_character_ ) # 过滤中间50%样本 valid_idx <- !is.na(gene_group) surv_sub <- Surv(df$time[valid_idx], df$status[valid_idx]) group_sub <- gene_group[valid_idx] # 确保分组是因子类型 group_sub <- factor(group_sub, levels = c("Low", "High")) # 执行log-rank检验 surv_diff <- survdiff(surv_sub ~ group_sub) # 提取p值(卡方检验) pval <- 1 - pchisq(surv_diff$chisq, df = length(surv_diff$n) - 1) return(data.frame(gene = gene_name, p_value = pval)) } # 应用函数到所有基因列 gene_columns <- setdiff(colnames(df), c("time", "status")) results <- purrr::map_dfr(gene_columns, analyze_gene) ``` #### 关键改进点 1. **数据类型验证**: ```r # 确保所有列正确类型 df <- df %>% mutate( time = as.numeric(time), status = as.numeric(status), across(all_of(gene_columns), as.numeric) ) ``` 2. **分组逻辑优化**: - 使用`case_when`替代嵌套`ifelse`,提高可读性和可靠性 - 明确设置因子水平:`levels = c("Low", "High")` 3. **生存对象创建**: ```r # 正确创建生存对象 surv_sub <- Surv(df$time[valid_idx], df$status[valid_idx]) ``` 4. **错误处理**: ```r # 添加分组验证 if (length(unique(na.omit(group_sub))) < 2) { warning(paste("Gene", gene_name, "has insufficient variation")) return(data.frame(gene = gene_name, p_value = NA)) } ``` #### 可视化生存曲线 ```r # 绘制特定基因的生存曲线 plot_survival <- function(gene_name) { # ...(同上分组逻辑)... # 创建生存拟合对象 fit <- survfit(surv_sub ~ group_sub) # 绘制Kaplan-Meier曲线 ggsurvplot(fit, data = data.frame(time = df$time[valid_idx], status = df$status[valid_idx], group = group_sub), pval = TRUE, risk.table = TRUE, title = paste("Survival by", gene_name, "Expression")) } # 示例:绘制第一个基因的生存曲线 plot_survival(gene_columns[1]) ``` #### 常见错误解决方案 | 错误原因 | 解决方案 | |----------|----------| | 非数值参数错误 | 使用`as.numeric()`转换所有数值列 | | 分组样本不足 | 添加`length(unique(group_sub)) >= 2`检查 | | 生存对象无效 | 确保`Surv()`接受数值型时间和状态 | | 分位数计算错误 | 添加`na.rm = TRUE`处理缺失值 | 通过以上方法,您应该能够正确实现基于基因表达25%和75%分位数的生存分析分组,并避免"二进列运算符中有非数值参数"的错误[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值