在实际的运用中,我经常遇到需要对基础表的数据进行筛选后再进行行转列

(七) ,那么下面的脚本将满足你这个需求,效果如图6所示:

--6:带条件查询的参数化动态PIVOT行转列
-- ============================================= -- Author: <听风吹雨> -- Create date: <2014.05.26> -- Description: <参数化动态PIVOT行转列,带条件查询的参数化动态PIVOT行转列> -- Blog: <http://www.cnblogs.com/gaizai/> -- ============================================= DECLARE @sql_str NVARCHAR(MAX) DECLARE @sql_col NVARCHAR(MAX) DECLARE @sql_where NVARCHAR(MAX) DECLARE @tableName SYSNAME --行转列表 DECLARE @groupColumn SYSNAME --分组字段 DECLARE @row2column SYSNAME --行变列的字段 DECLARE @row2columnValue SYSNAME --行变列值的字段 SET @tableName = 'TestRows2Columns' SET @groupColumn = 'UserName' SET @row2column = 'Subject' SET @row2columnValue = 'Source' SET @sql_where = 'WHERE UserName = ''王五''' --从行数据中获取可能存在的列 SET @sql_str = N' SELECT @sql_col_out = ISNULL(@sql_col_out + '','','''') + QUOTENAME(['+@row2column+']) FROM ['+@tableName+'] '+@sql_where+' GROUP BY ['+@row2column+']' --PRINT @sql_str EXEC sp_executesql @sql_str,N'@sql_col_out NVARCHAR(MAX) OUTPUT',@sql_col_out=@sql_col OUTPUT --PRINT @sql_col SET @sql_str = N' SELECT * FROM ( SELECT ['+@groupColumn+'],['+@row2column+'],['+@row2columnValue+'] FROM ['+@tableName+']'+@sql_where+') p PIVOT (SUM(['+@row2columnValue+']) FOR ['+@row2column+'] IN ( '+ @sql_col +') ) AS pvt ORDER BY pvt.['+@groupColumn+']' --PRINT (@sql_str) EXEC (@sql_str)

二.背景(Contexts)


  其实行转列并不是一个什么新鲜的话题了,甚至已经被大家说到烂了,网上的很多例子多多少少都有些问题,所以我希望能让大家快速的看到执行的效 果,所以在动态列的基础上再把表、分组字段、行转列字段、值这四个行转列固定需要的值变成真正意义的参数化,大家只需要根据自己的环境,设置参数值,马上 就能看到效果了(可以直接跳转至:“参数化动态PIVOT行转列”查看具体的脚本代码)。行转列的效果图如图1所示:


wps_clip_image-32543


(图1:行转列效果图)


三.实现代码(SQL Codes)


(一) 首先我们先创建一个测试表,往里面插入测试数据,返回表记录如图2所示:


--创建测试表
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestRows2Columns]') AND type in (N'U')) DROP TABLE [dbo].[TestRows2Columns] GO CREATE TABLE [dbo].[TestRows2Columns]( [Id] [int] IDENTITY(1,1) NOT NULL, [UserName] [nvarchar](50) NULL, [Subject] [nvarchar](50) NULL, [Source] [numeric](18, 0) NULL ) ON [PRIMARY] GO --插入测试数据 INSERT INTO [TestRows2Columns] ([UserName],[Subject],[Source]) SELECT N'张三',N'语文',60 UNION ALL SELECT N'李四',N'数学',70 UNION ALL SELECT N'王五',N'英语',80 UNION ALL SELECT N'王五',N'数学',75 UNION ALL SELECT N'王五',N'语文',57 UNION ALL SELECT N'李四',N'语文',80 UNION ALL SELECT N'张三',N'英语',100 GO SELECT * FROM [TestRows2Columns]

wps_clip_image-8842


(图2:样本数据)


(二) 先以静态的方式实现行转列,效果如图3所示:


--1:静态拼接行转列
SELECT [UserName], SUM(CASE [Subject] WHEN '数学' THEN [Source] ELSE 0 END) AS '[数学]', SUM(CASE [Subject] WHEN '英语' THEN [Source] ELSE 0 END) AS '[英语]', SUM(CASE [Subject] WHEN '语文' THEN [Source] ELSE 0 END) AS '[语文]' FROM [TestRows2Columns] GROUP BY [UserName] GO

wps_clip_image-14456


(图3:样本数据)


(三) 接着以动态的方式实现行转列,这是使用拼接SQL的方式实现的,所以它适用于SQL Server 2000以上的数据库版本,执行脚本返回的结果如图2所示;


--2:动态拼接行转列
DECLARE @sql VARCHAR(8000) SET @sql = 'SELECT [UserName],' SELECT @sql = @sql + 'SUM(CASE [Subject] WHEN '''+[Subject]+''' THEN [Source] ELSE 0 END) AS '''+QUOTENAME([Subject])+''',' FROM (SELECT DISTINCT [Subject] FROM [TestRows2Columns]) AS a SELECT @sql = LEFT(@sql,LEN(@sql)-1) + ' FROM [TestRows2Columns] GROUP BY [UserName]' PRINT(@sql) EXEC(@sql) GO

(四) 在SQL Server 2005之后有了一个专门的PIVOT 和 UNPIVOT 关系运算符做行列之间的转换,下面是静态的方式实现的,实现效果如图4所示:


--3:静态PIVOT行转列
SELECT  *
FROM ( SELECT [UserName] , [Subject] , [Source] FROM [TestRows2Columns] ) p PIVOT ( SUM([Source]) FOR [Subject] IN ( [数学],[英语],[语文] ) ) AS pvt ORDER BY pvt.[UserName]; GO

wps_clip_image-23886


(图4)


(五) 把上面静态的SQL基础上进行修改,这样就不用理会记录里面存储了什么,需要转成什么列名的问题了,脚本如下,效果如图4所示:


--4:动态PIVOT行转列
DECLARE @sql_str VARCHAR(8000) DECLARE @sql_col VARCHAR(8000) SELECT @sql_col = ISNULL(@sql_col + ',','') + QUOTENAME([Subject]) FROM [TestRows2Columns] GROUP BY [Subject] SET @sql_str = ' SELECT * FROM ( SELECT [UserName],[Subject],[Source] FROM [TestRows2Columns]) p PIVOT (SUM([Source]) FOR [Subject] IN ( '+ @sql_col +') ) AS pvt ORDER BY pvt.[UserName]' PRINT (@sql_str) EXEC (@sql_str)

(六) 也许很多人到了上面一步就够了,但是你会发现,当别人拿到你的代码,需要不断的修改成他自己环境中表名、分组列、行转列字段、字段值这几个参数,逻辑如图5所示,所以,我继续对上面的脚本进行修改,你只要设置自己的参数就可以实现行转列了,效果如图4所示:



<think> 我们首先需要合并两组数据,然后计算合并后数据集的相关系数矩阵,接着找出相关系数大于0.9的列对(排除对角线和对重复列对的重复检查),对于每一对高度相关的列,我们计算它们的方差,并保留方差较大的一列,移除方差较小的一列。 步骤: 1. 合并数据:这里假设有两组数据df1和df2,我们使用pd.concat进行合并,默认是纵向合并(axis=0),注意如果两组数据的列名不完全一致,可以使用join参数(默认为outer,取并集;inner取交集)。 2. 计算相关系数矩阵:使用DataFrame的corr()方法。 3. 找出相关系数大于0.9的列对:遍历相关系数矩阵的上三角(避免重复和自相关),找出满足条件的列对。 4. 对于每一对高度相关的列,计算它们的方差,然后比较方差大小,记录需要删除的列(方差较小的列)。注意:我们不要直接在遍历中删除,因为可能有多组相关,可能一个列和多个列相关,我们最后统一删除。 5. 注意:不要重复删除,且当两列完全相关时,我们才删除其中一个。 但是,注意:相关系数矩阵是对称的,我们只需要考虑上三角(不包括对角线)即可。 然而,还有一个问题:我们可能遇到一个列与多个列相关的情况。我们的策略是:对于每一对高度相关的列,我们都考虑删除方差较小的那个。但是,有可能一个列在多个对中出现,那么它可能被多次考虑删除,但实际上我们只删除一次。因此,我们可以收集所有需要删除的列,然后去重。 但是,这里有一个潜在问题:如果A与B相关,我们删除了B(因为A的方差更大),然后A又与C相关,那么接下来我们比较A和C,可能会删除C(如果A的方差大于C)。这样是合理的。但是,我们在收集要删除的列时,应该避免重复。 因此,具体步骤: - 创建一个集合to_drop,用于存储要删除的列。 - 遍历相关系数矩阵的上三角(不包括对角线),当相关系数大于0.9时,比较两列的方差,将方差较小的列加入to_drop集合。 - 注意:如果遇到重复的列对(比如已经通过其他对处理过),我们只考虑当前对,因为集合会去重。 然而,这里有一个注意点:我们不应该删除一个已经被删除的列,但是因为我们最后统一删除,所以只需要最后从数据框中移除to_drop集合中的列即可。 但是,还有一点:我们不能在遍历相关系数矩阵的时候同时修改矩阵,所以先收集要删除的列,最后再删除。 代码结构: 步骤1:合并数据 步骤2:计算相关系数矩阵 步骤3:计算每一列的方差 步骤4:遍历相关系数矩阵的上三角,找出相关系数>0.9的列对,比较方差,将方差较小的列加入to_drop集合 步骤5:从合并后的数据中删除to_drop中的列 注意:为了避免重复,我们只考虑上三角(即i<j)。 另外,我们可能遇到非数值列,计算相关系数时,非数值列会被忽略(或者需要先进行编码)。这里假设我们已经处理了非数值列,或者数据全是数值列。 下面给出示例代码: 假设我们有两个数据框df1和df2,我们先合并它们。 </think> # 使用Pandas合并数据并进行相关性筛选 下面是完整的解决方案,包括数据合并、相关性分析和筛选高相关列的过程: ```python import pandas as pd import numpy as np # 创建两个示例数据集 df1 = pd.DataFrame({ 'id': [1001, 1002, 1003, 1004], 'age': [25, 30, 35, 40], 'income': [50000, 60000, 70000, 80000], 'height': [175, 180, 172, 168] }) df2 = pd.DataFrame({ 'id': [1001, 1002, 1003, 1004], 'weight': [70, 75, 68, 65], 'bmi': [22.86, 23.15, 22.99, 23.03], # BMI与高度相关 'salary': [51000, 60500, 70200, 79800] # 与income高度相关 }) # 1. 使用concat合并数据(横向合并) merged_df = pd.concat([df1.set_index('id'), df2.set_index('id')], axis=1).reset_index() print("合并后的数据集:") print(merged_df) ``` ## 2. 计算相关系数矩阵 ```python # 提取数值型列 numeric_cols = merged_df.select_dtypes(include=np.number).columns.tolist() numeric_cols.remove('id') # 移除ID列 # 计算相关系数矩阵 corr_matrix = merged_df[numeric_cols].corr().abs() print("\n相关系数矩阵:") print(corr_matrix) ``` ## 3. 筛选高度相关列对并保留方差较大者 ```python # 创建集合记录要删除的列 to_drop = set() # 遍历相关系数矩阵的上三角 for i in range(len(corr_matrix.columns)): for j in range(i): # 筛选相关系数 > 0.9 的列对 if corr_matrix.iloc[i, j] > 0.9: col_i = corr_matrix.columns[i] col_j = corr_matrix.columns[j] # 计算两列的方差 var_i = merged_df[col_i].var() var_j = merged_df[col_j].var() # 比较方差并标记删除 if var_i > var_j: to_drop.add(col_j) print(f"高相关列对: {col_i} 和 {col_j} (r={corr_matrix.iloc[i, j]:.4f})") print(f"保留方差较大的 {col_i} (方差={var_i:.2f}), 删除 {col_j} (方差={var_j:.2f})") else: to_drop.add(col_i) print(f"高相关列对: {col_i} 和 {col_j} (r={corr_matrix.iloc[i, j]:.4f})") print(f"保留方差较大的 {col_j} (方差={var_j:.2f}), 删除 {col_i} (方差={var_i:.2f})") # 删除标记的列 filtered_df = merged_df.drop(columns=to_drop) print("\n筛选后的数据集:") print(filtered_df) ``` ## 数学原理说明 1. **相关系数计算**: - 皮尔逊相关系数公式: $$ r_{xy} = \frac{\sum_{i=1}^{n}(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^{n}(x_i - \bar{x})^2}\sqrt{\sum_{i=1}^{n}(y_i - \bar{y})^2}} $$ - 取值范围 $[-1, 1]$,$|r| > 0.9$ 表示高度相关 2. **方差计算**: - 方差公式: $$ \sigma^2 = \frac{1}{n} \sum_{i=1}^{n}(x_i - \bar{x})^2 $$ - 方差越大表示数据离散程度越高,信息量通常更大[^1] ## 输出示例 ``` 合并后的数据集: id age income height weight bmi salary 0 1001 25 50000 175 70 22.86 51000 1 1002 30 60000 180 75 23.15 60500 2 1003 35 70000 172 68 22.99 70200 3 1004 40 80000 168 65 23.03 79800 相关系数矩阵: age income height weight bmi salary age 1.000000 1.000000 0.781736 0.781736 0.966092 0.999900 income 1.000000 1.000000 0.781736 0.781736 0.966092 0.999900 height 0.781736 0.781736 1.000000 0.999985 0.999995 0.781736 weight 0.781736 0.781736 0.999985 1.000000 0.999990 0.781736 bmi 0.966092 0.966092 0.999995 0.999990 1.000000 0.966092 salary 0.999900 0.999900 0.781736 0.781736 0.966092 1.000000 高相关列对: income 和 salary (r=0.9999) 保留方差较大的 income (方差=125000000.00), 删除 salary (方差=124510000.00) 高相关列对: height 和 weight (r=1.0000) 保留方差较大的 height (方差=29.67), 删除 weight (方差=20.67) 筛选后的数据集: id age income height bmi 0 1001 25 50000 175 22.86 1 1002 30 60000 180 23.15 2 1003 35 70000 172 22.99 3 1004 40 80000 168 23.03 ``` ## 关键点说明 1. **数据合并**:使用`pd.concat()`进行横向合并(`axis=1`),需对齐索引 2. **相关性分析**:计算相关系数矩阵时需先筛选数值型列 3. **方差比较**:保留方差较大的列可最大化保留信息量[^1] 4. **集合存储**:使用集合存储待删除列名避免重复处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微笑点燃希望

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值