普通行列转换

本文介绍如何使用SQLServer 2000及2005进行成绩表的行列转换,包括静态和动态SQL的实现方式,并增加了平均分和总分的计算。

 说明:普通行列转换(version 1.0)仅针对sql server 2000提供静态和动态写法,version 2.0增加sql server 2005的有关写法。

问题:假设有张学生成绩表(tb)如下:
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94
想变成(得到如下结果):
姓名 语文 数学 物理
---- ---- ---- ----
李四 74   84   94
张三 74   83   93
-------------------
*/

create table tb(姓名 varchar(10) , 课程 varchar(10) , 分数 int)
insert into tb values('张三' , '语文' , 74)
insert into tb values('张三' , '数学' , 83)
insert into tb values('张三' , '物理' , 93)
insert into tb values('李四' , '语文' , 74)
insert into tb values('李四' , '数学' , 84)
insert into tb values('李四' , '物理' , 94)
go

--SQL SERVER 2000 静态SQL,指课程只有语文、数学、物理这三门课程。(以下同)
select 姓名 as 姓名 ,
  max(case 课程 when '语文' then 分数 else 0 end) 语文,
  max(case 课程 when '数学' then 分数 else 0 end) 数学,
  max(case 课程 when '物理' then 分数 else 0 end) 物理
from tb
group by 姓名

--SQL SERVER 2000 动态SQL,指课程不止语文、数学、物理这三门课程。(以下同)
declare @sql varchar(8000)
set @sql = 'select 姓名 '
select @sql = @sql + ' , max(case 课程 when ''' + 课程 + ''' then 分数 else 0 end) [' + 课程 + ']'
from (select distinct 课程 from tb) as a
set @sql = @sql + ' from tb group by 姓名'
exec(@sql)

--SQL SERVER 2005 静态SQL。
select * from (select * from tb) a pivot (max(分数) for 课程 in (语文,数学,物理)) b

--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + '],[' , '') + 课程 from tb group by 课程
set @sql = '[' + @sql + ']'
exec ('select * from (select * from tb) a pivot (max(分数) for 课程 in (' + @sql + ')) b')

---------------------------------

/*
问题:在上述结果的基础上加平均分,总分,得到如下结果:
姓名 语文 数学 物理 平均分 总分
---- ---- ---- ---- ------ ----
李四 74   84   94   84.00  252
张三 74   83   93   83.33  250
*/

--SQL SERVER 2000 静态SQL。
select 姓名 姓名,
  max(case 课程 when '语文' then 分数 else 0 end) 语文,
  max(case 课程 when '数学' then 分数 else 0 end) 数学,
  max(case 课程 when '物理' then 分数 else 0 end) 物理,
  cast(avg(分数*1.0) as decimal(18,2)) 平均分,
  sum(分数) 总分
from tb
group by 姓名

--SQL SERVER 2000 动态SQL。
declare @sql varchar(8000)
set @sql = 'select 姓名 '
select @sql = @sql + ' , max(case 课程 when ''' + 课程 + ''' then 分数 else 0 end) [' + 课程 + ']'
from (select distinct 课程 from tb) as a
set @sql = @sql + ' , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名'
exec(@sql)

--SQL SERVER 2005 静态SQL。
select m.* , n.平均分 , n.总分 from
(select * from (select * from tb) a pivot (max(分数) for 课程 in (语文,数学,物理)) b) m,
(select 姓名 , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名) n
where m.姓名 = n.姓名

--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + ',' , '') + 课程 from tb group by 课程
exec ('select m.* , n.平均分 , n.总分 from
(select * from (select * from tb) a pivot (max(分数) for 课程 in (' + @sql + ')) b) m ,
(select 姓名 , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名) n
where m.姓名 = n.姓名')

drop table tb   

------------------
------------------

/*
问题:如果上述两表互相换一下:即表结构和数据为:
姓名 语文 数学 物理
张三 74  83  93
李四 74  84  94
想变成(得到如下结果):
姓名 课程 分数
---- ---- ----
李四 语文 74
李四 数学 84
李四 物理 94
张三 语文 74
张三 数学 83
张三 物理 93
--------------
*/

create table tb(姓名 varchar(10) , 语文 int , 数学 int , 物理 int)
insert into tb values('张三',74,83,93)
insert into tb values('李四',74,84,94)
go

--SQL SERVER 2000 静态SQL。
select * from
(
 select 姓名 , 课程 = '语文' , 分数 = 语文 from tb
 union all
 select 姓名 , 课程 = '数学' , 分数 = 数学 from tb
 union all
 select 姓名 , 课程 = '物理' , 分数 = 物理 from tb
) t
order by 姓名 , case 课程 when '语文' then 1 when '数学' then 2 when '物理' then 3 end

--SQL SERVER 2000 动态SQL。
--调用系统表动态生态。
declare @sql varchar(8000)
select @sql = isnull(@sql + ' union all ' , '' ) + ' select 姓名 , [课程] = ' + quotename(Name , '''') + ' , [分数] = ' + quotename(Name) + ' from tb'
from syscolumns
where name! = N'姓名' and ID = object_id('tb') --表名tb,不包含列名为姓名的其它列
order by colid asc
exec(@sql + ' order by 姓名 ')

--SQL SERVER 2005 动态SQL。
select 姓名 , 课程 , 分数 from tb unpivot (分数 for 课程 in([语文] , [数学] , [物理])) t

--SQL SERVER 2005 动态SQL,同SQL SERVER 2000 动态SQL。

--------------------
/*
问题:在上述的结果上加个平均分,总分,得到如下结果:
姓名 课程   分数
---- ------ ------
李四 语文   74.00
李四 数学   84.00
李四 物理   94.00
李四 平均分 84.00
李四 总分   252.00
张三 语文   74.00
张三 数学   83.00
张三 物理   93.00
张三 平均分 83.33
张三 总分   250.00
------------------
*/

select * from
(
 select 姓名 as 姓名 , 课程 = '语文' , 分数 = 语文 from tb
 union all
 select 姓名 as 姓名 , 课程 = '数学' , 分数 = 数学 from tb
 union all
 select 姓名 as 姓名 , 课程 = '物理' , 分数 = 物理 from tb
 union all
 select 姓名 as 姓名 , 课程 = '平均分' , 分数 = cast((语文 + 数学 + 物理)*1.0/3 as decimal(18,2)) from tb
 union all
 select 姓名 as 姓名 , 课程 = '总分' , 分数 = 语文 + 数学 + 物理 from tb
) t
order by 姓名 , case 课程 when '语文' then 1 when '数学' then 2 when '物理' then 3 when '平均分' then 4 when '总分' then 5 end

drop table tb

<think>我们正在处理用户关于`rownames_to_column`和`column_to_rownames`函数的使用问题。根据用户提供的引用和之前对话历史,我们可以总结如下: 1. 这两个函数来自`tibble`包,因此在使用前需要加载该包(`library(tibble)`)[^2]。 2. `rownames_to_column`用于将数据框的行名转换为一个新的列,而`column_to_rownames`则是将某一列转换为行名[^1]。 3. 用户希望了解如何具体使用这两个函数进行行列转换。 我们将通过以下步骤进行说明: - 安装和加载必要的包 - 创建示例数据框(带行名) - 演示`rownames_to_column`将行名转为列 - 演示`column_to_rownames`将列转为行名 - 注意事项和常见问题 注意:在R中,数据框(data.frame)和tibble(tbl_df)都可以使用这两个函数,但tibble是tidyverse中的现代数据框,推荐使用。 ### 步骤1: 安装与加载包 如果尚未安装`tibble`包,请先安装。然后加载包: ```r # 安装tibble包(如果未安装) # install.packages("tibble") # 加载包 library(tibble) ``` ### 步骤2: 创建示例数据 创建一个带有行名的数据框: ```r # 创建一个数据框,行名为字母,两列数据 df <- data.frame( Value1 = c(10, 20, 30), Value2 = c(15, 25, 35), row.names = c("A", "B", "C") # 设置行名 ) # 查看数据 print(df) # Value1 Value2 # A 10 15 # B 20 25 # C 30 35 ``` ### 步骤3: 使用`rownames_to_column`将行名转换为列 将行名转换为一个名为"ID"的新列: ```r # 将行名转换为列 df_with_id <- rownames_to_column(df, var = "ID") # 查看转换后的数据 print(df_with_id) # ID Value1 Value2 # 1 A 10 15 # 2 B 20 25 # 3 C 30 35 ``` 注意:参数`var`用于指定新列的名称,如果不指定,默认为"rowname"。 ### 步骤4: 使用`column_to_rownames`将列转换为行名 现在,我们将"ID"列转换回行名: ```r # 将"ID"列转换为行名 df_restored <- column_to_rownames(df_with_id, var = "ID") # 查看恢复后的数据 print(df_restored) # Value1 Value2 # A 10 15 # B 20 25 # C 30 35 ``` 此时,数据框又恢复了最初的行名。 ### 注意事项 1. **包加载问题**:如果遇到函数未找到的错误(如`could not find function "column_to_rownames"`),请确保已正确加载`tibble`包[^2]。 2. **行名唯一性**:在将列转换为行名时,该列的值必须是唯一的,否则会报错。 3. **数据框类型**:`tibble`与传统的`data.frame`在打印和子集选取上有所不同,但这两个函数在两种类型上都能使用。 4. **管道操作**:在`tidyverse`中,我们可以使用管道`%>%`来链式调用: ```r library(dplyr) df %>% rownames_to_column("ID") %>% mutate(Value3 = Value1 + Value2) %>% # 添加新列 column_to_rownames("ID") ``` ### 实际应用场景 在生物信息学中,我们经常需要将基因名(作为行名)转换为列,以便进行数据整理(如使用`dplyr`包),然后再转换回行名以满足某些分析包(如`limma`)的要求[^1]。 ### 常见问题解答 **问题1:转换后数据框的行名顺序会改变吗?** 不会,转换操作不会改变行的顺序,只是将行名移动到一个新列,或者将一列的值设置为行名。 **问题2:转换后原始数据会改变吗?** 这些函数不会修改原始数据框,而是返回一个新的数据框。因此,需要将结果赋值给新变量或覆盖原变量。 **问题3:除了`tibble`包,还有其他方法吗?** 是的,基础R中也有类似操作: - 将行名转为列:`df$new_col <- rownames(df)` - 将列转为行名:`rownames(df) <- df$col; df$col <- NULL` 但使用`tibble`包中的函数更安全(如避免行名重复问题)且易于在管道中使用。 ### 总结 - `rownames_to_column()`:行名→列,参数`var`指定新列名。 - `column_to_rownames()`:列→行名,参数`var`指定要转换的列名。 通过这两个函数,可以方便地在行名和列之间转换,特别适合在数据整理和特定分析需求之间切换。 §§相关问题§§ 1. 在tidyverse中如何避免行名转换时出现重复值? 2. 使用column_to_rownames函数后,如何保存修改后的数据框? 3. 除了tibble包,还有哪些R包提供了类似的行列转换功能?</think>### 在R中使用`rownames_to_column`和`column_to_rownames`进行行列转换 #### 1. **函数基础用法** 这两个函数来自`tibble`包,用于数据框的行名与列数据之间的转换: ```r library(tibble) # 必须加载包[^2] # 创建带行名的示例数据 df <- data.frame( Value = c(1.2, 0.8, 1.5), row.names = c("GeneA", "GeneB", "GeneC") ) # 将行名转换为新列"Gene" df_new <- rownames_to_column(df, var = "Gene") # 将"Gene"列转回行名 df_restored <- column_to_rownames(df_new, var = "Gene") ``` #### 2. **详细参数说明** | 函数 | 参数 | 作用 | 示例 | |-----------------------|---------|-------------------------------|--------------------------| | `rownames_to_column()`| `var` | 指定新列名(默认"rowname") | `var = "Gene_ID"` | | `column_to_rownames()`| `var` | 指定要转为行名的列名 | `var = "Sample_ID"` | #### 3. **生物信息学应用案例** **基因表达矩阵处理流程:** ```r # 原始数据(行名为基因名) expression_data <- data.frame( Sample1 = rnorm(3), Sample2 = rnorm(3), row.names = c("TP53", "BRCA1", "EGFR") ) # 步骤1: 行名转列(便于tidyverse操作) tidy_data <- rownames_to_column(expression_data, "Gene") # 步骤2: 数据处理(示例:过滤低表达基因) filtered_data <- tidy_data %>% filter(Sample1 > 0 & Sample2 > 0) # 步骤3: 转回行名格式(供DESeq2等包使用) final_data <- column_to_rownames(filtered_data, "Gene") ``` #### 4. **注意事项与常见问题** 1. **包依赖问题**: ```r # 错误处理:函数未找到 if(!require(tibble)) install.packages("tibble") # 引用[2] ``` 2. **行名唯一性检查**: ```r # 确保转换列无重复值 df %>% distinct(Gene, .keep_all = TRUE) %>% # 去重 column_to_rownames("Gene") ``` 3. **数据类型保留**: ```r # 转换后验证数据类型 str(column_to_rownames(df, "Gene")) ``` 4. **管道操作最佳实践**: ```r mtcars %>% rownames_to_column("Car") %>% filter(mpg > 20) %>% mutate(CarType = ifelse(grepl("Toyota", Car), "Japanese", "Other")) %>% column_to_rownames("Car") ``` #### 5. **与传统方法的对比** | 方法 | 优点 | 缺点 | |---------------------|---------------------------|---------------------------| | `rownames_to_column`| 保留行名数据,兼容tidyverse | 需额外加载包 | | 基础R方法 | 无需额外包 | 代码冗长,易出错 | ```r # 基础R实现相同功能 df$Gene <- rownames(df) # 行名转列 rownames(df) <- df$Gene # 列转行名 df$Gene <- NULL # 需手动删除原列 ``` > **关键提示**:在生物信息分析中,建议仅在需要行名的第三方包(如DESeq2、limma)中使用`column_to_rownames`,在数据清洗阶段保持标识列为普通数据列[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值