Pandas dataframe 将特征划分为高相关性组
Pandas dataframe divide features to group of high correlation
我有一个包含 280 多个特征的数据框。
我 运行 相关图检测高度相关的特征组:
现在,我想将特征分成几组,这样每个组都是一个“红色区域”,这意味着每个组都将具有彼此相关 >0.5 的特征。
如何做到?
谢谢
免责声明:
- 此解决方案未解决可视化问题。仅找到群组。
- 已知解决方案是 NP-hard,所以请注意效率问题。
理论
问题本质上是图论中的一个clique problem问题,意思是在给定的图中(节点> 2)找到所有完整的子图。
想象一个图,所有特征都是节点,满足 corr > 0.5
的特征对是边。那么找到所有请求的“组”的任务可以简单地转化为“找到图中所有完整的子图”。
代码
根据文档,代码使用 networkx.algorithms.find_cliques for the search task, which implements Bron–Kerbosch algorithm。
代码由两部分组成。第一部分使用 np.triu
(从 修改)提取边缘,第二部分将边缘列表馈送到 networkx
.
相关矩阵
特征 [A,B,C] 和 [C,D,E] 分别密切相关,但 [A,B] 和 [D,E] 之间不相关。
np.random.seed(111) # reproducibility
x = np.random.normal(0, 1, 100)
y = np.random.normal(0, 1, 100)
a = x
b = x + np.random.normal(0, .5, 100)
c = x + y
d = y + np.random.normal(0, .5, 100)
e = y + np.random.normal(0, .5, 100)
df = pd.DataFrame({"A":a, "B":b, "C":c, "D":d, "E":e})
corr = df.corr()
corr
Out[24]:
A B C D E
A 1.000000 0.893366 0.677333 -0.078369 -0.090510
B 0.893366 1.000000 0.577459 -0.072025 -0.079855
C 0.677333 0.577459 1.000000 0.587695 0.579891
D -0.078369 -0.072025 0.587695 1.000000 0.777803
E -0.090510 -0.079855 0.579891 0.777803 1.000000
第 1 部分
# keep only upper triangle elements (excluding diagonal elements)
mask_keep = np.triu(np.ones(corr.shape), k=1).astype('bool').reshape(corr.size)
# melt (unpivot) the dataframe and apply mask
sr = corr.stack()[mask_keep]
# filter and get names
edges = sr[sr > 0.5].reset_index().values[:, :2]
edges
Out[25]:
array([['A', 'B'],
['A', 'C'],
['B', 'C'],
['C', 'D'],
['C', 'E'],
['D', 'E']], dtype=object)
第 2 部分
import networkx as nx
g = nx.from_edgelist(edges)
ls_cliques = []
for clique in nx.algorithms.find_cliques(g):
ls_cliques.append(clique)
# result
ls_cliques
Out[26]: [['C', 'A', 'B'], ['C', 'D', 'E']]
我在这里遇到了同样的问题:堆叠相关矩阵的长度与掩码的长度不同。
对我有用的是在堆叠时保持 NaN,如下所示:
sr = corr.stack([dropna=False][1])[mask_keep]
@billhuang 正确说明了发生这种情况的原因。
我有一个包含 280 多个特征的数据框。
我 运行 相关图检测高度相关的特征组:
如何做到?
谢谢
免责声明:
- 此解决方案未解决可视化问题。仅找到群组。
- 已知解决方案是 NP-hard,所以请注意效率问题。
理论
问题本质上是图论中的一个clique problem问题,意思是在给定的图中(节点> 2)找到所有完整的子图。
想象一个图,所有特征都是节点,满足 corr > 0.5
的特征对是边。那么找到所有请求的“组”的任务可以简单地转化为“找到图中所有完整的子图”。
代码
根据文档,代码使用 networkx.algorithms.find_cliques for the search task, which implements Bron–Kerbosch algorithm。
代码由两部分组成。第一部分使用 np.triu
(从 networkx
.
相关矩阵
特征 [A,B,C] 和 [C,D,E] 分别密切相关,但 [A,B] 和 [D,E] 之间不相关。
np.random.seed(111) # reproducibility
x = np.random.normal(0, 1, 100)
y = np.random.normal(0, 1, 100)
a = x
b = x + np.random.normal(0, .5, 100)
c = x + y
d = y + np.random.normal(0, .5, 100)
e = y + np.random.normal(0, .5, 100)
df = pd.DataFrame({"A":a, "B":b, "C":c, "D":d, "E":e})
corr = df.corr()
corr
Out[24]:
A B C D E
A 1.000000 0.893366 0.677333 -0.078369 -0.090510
B 0.893366 1.000000 0.577459 -0.072025 -0.079855
C 0.677333 0.577459 1.000000 0.587695 0.579891
D -0.078369 -0.072025 0.587695 1.000000 0.777803
E -0.090510 -0.079855 0.579891 0.777803 1.000000
第 1 部分
# keep only upper triangle elements (excluding diagonal elements)
mask_keep = np.triu(np.ones(corr.shape), k=1).astype('bool').reshape(corr.size)
# melt (unpivot) the dataframe and apply mask
sr = corr.stack()[mask_keep]
# filter and get names
edges = sr[sr > 0.5].reset_index().values[:, :2]
edges
Out[25]:
array([['A', 'B'],
['A', 'C'],
['B', 'C'],
['C', 'D'],
['C', 'E'],
['D', 'E']], dtype=object)
第 2 部分
import networkx as nx
g = nx.from_edgelist(edges)
ls_cliques = []
for clique in nx.algorithms.find_cliques(g):
ls_cliques.append(clique)
# result
ls_cliques
Out[26]: [['C', 'A', 'B'], ['C', 'D', 'E']]
我在这里遇到了同样的问题:堆叠相关矩阵的长度与掩码的长度不同。 对我有用的是在堆叠时保持 NaN,如下所示:
sr = corr.stack([dropna=False][1])[mask_keep]
@billhuang 正确说明了发生这种情况的原因。