在 python 中读取矩阵并获取行名和列名

Posted

技术标签:

【中文标题】在 python 中读取矩阵并获取行名和列名【英文标题】:reading a matrix and fetch row and column names in python 【发布时间】:2016-02-05 12:24:54 【问题描述】:

我想读取一个矩阵文件,如下所示:

sample  sample1 sample2 sample3
sample1 1   0.7 0.8
sample2 0.7 1   0.8
sample3 0.8 0.8 1

我想获取所有值 > 0.8 的对。例如:sample1,sample3 0.8sample2,sample3 0.8 等在一个大文件中。

当我使用csv.reader 时,每一行都会变成一个列表,并且跟踪行和列名会使程序变得狡猾。我想知道一种优雅的方法,比如使用numpypandas

期望的输出:

sample1,sample3 0.8 
sample2,sample3 0.8

1 可以忽略,因为在同一个样本之间,它总是为 1。

【问题讨论】:

pandas 和 numpy 都有 csv 阅读器。有很多关于这些的问题。 【参考方案1】:

您可以使用np.triu 屏蔽掉上三角值:

In [11]: df
Out[11]:
         sample1  sample2  sample3
sample
sample1      1.0      0.7      0.8
sample2      0.7      1.0      0.8
sample3      0.8      0.8      1.0

In [12]: np.triu(df, 1)
Out[12]:
array([[ 0. ,  0.7,  0.8],
       [ 0. ,  0. ,  0.8],
       [ 0. ,  0. ,  0. ]])

In [13]: np.triu(df, 1) >= 0.8
Out[13]:
array([[False, False,  True],
       [False, False,  True],
       [False, False, False]], dtype=bool)

然后提取正确的索引/列我认为你必须使用np.where*:

In [14]: np.where(np.triu(df, 1) >= 0.8)
Out[14]: (array([0, 1]), array([2, 2]))

这为您提供了一个包含第一个索引索引和列索引的数组(这是此 numpy 版本中效率最低的部分):

In [16]: index, cols = np.where(np.triu(df, 1) >= 0.8)

In [17]: [(df.index[i], df.columns[j], df.iloc[i, j]) for i, j in zip(index, cols)]
Out[17]:
[('sample1', 'sample3', 0.80000000000000004),
 ('sample2', 'sample3', 0.80000000000000004)]

根据需要。

*我可能忘记了获取最后一个块的更简单方法(编辑:下面的 pandas 代码可以做到这一点,但我认为可能还有另一种方法。)


您可以在 pandas 中使用相同的技巧,但使用堆栈来本地获取索引/列:

In [21]: (np.triu(df, 1) >= 0.8) * df
Out[21]:
         sample1  sample2  sample3
sample
sample1        0        0      0.8
sample2        0        0      0.8
sample3        0        0      0.0

In [22]: res = ((np.triu(df, 1) >= 0.8) * df).stack()

In [23]: res
Out[23]:
sample
sample1  sample1    0.0
         sample2    0.0
         sample3    0.8
sample2  sample1    0.0
         sample2    0.0
         sample3    0.8
sample3  sample1    0.0
         sample2    0.0
         sample3    0.0
dtype: float64

In [24]: res[res!=0]
Out[24]:
sample
sample1  sample3    0.8
sample2  sample3    0.8
dtype: float64

【讨论】:

【参考方案2】:

如果您想使用 Pandas,以下答案会有所帮助。我假设您会自己弄清楚如何将矩阵文件读入 Pandas。我还假设您的列和行标记正确。读取数据后,您最终会得到一个 DataFrame,它看起来很像您在问题顶部的矩阵。我假设所有行名都是 DataFrame 索引。我以您已将数据读入一个名为df 的变量作为我的起点。

Pandas 按行比按列更有效。因此,我们按行执行操作,循环遍历列。

pairs = 
for col in df.columns:
    pairs[col] = df[(df[col] >= 0.8) & (df[col] < 1)].index.tolist()
    # If row names are not an index, but a different column named 'names' run the following line, instead of the line above
    # pairs[col] = df[(df[col] >= 0.8) & (df[col] < 1)]['names'].tolist()

或者,您可以使用apply() 来执行此操作,因为这也会遍历所有列。 (也许在 0.17 会释放 GIL 以获得更快的结果,我不知道,因为我没有尝试过。)

pairs 现在将包含作为键的列名和作为相关性大于 0.8 但小于 1 的值的行名列表。

如果您还想从 DataFrame 中提取相关值,请将 .tolist() 替换为 .to_dict().to_dict() 将生成一个字典,索引是键,值是值:index -&gt; value。所以,最终你的pairs 看起来像column -&gt; index -&gt; value。它也将保证免费nan。请注意,.to_dict() 仅在您的索引包含您想要的行名时才有效,否则它将返回默认索引,即数字。


附言。如果您的文件很大,我建议您分块阅读。在这种情况下,上面的代码将针对每个块重复。所以它应该在你的循环中迭代块。但是,您必须小心地将来自下一个块的新数据附加到pairs。以下链接供您参考:

    Pandas I/O docs Pandas read_csv() function SO question on chunked read

您可能还想阅读参考资料 1,了解 Pandas 支持的其他类型的 I/O。

【讨论】:

【参考方案3】:

要读取它,您需要 skipinitialspaceindex_col 参数:

a=pd.read_csv('yourfile.txt',sep=' ',skipinitialspace=True,index_col=0)

要成对地获取值:

[[x,y,round(a[x][y],3)] for x in a.index for y in a.columns if x!=y and a[x][y]>=0.8][:2]

给予:

[['sample1', 'sample3', 0.8], 
['sample2', 'sample3', 0.8]]

【讨论】:

【参考方案4】:

使用scipy.sparse.coo_matrix,因为它适用于“(行,列)数据”格式。

from scipy.sparse import coo_matrix
import numpy as np

M = np.matrix([[1.0, 0.7, 0.8], [0.7, 1.0, 0.8], [0.8, 0.8, 1.0]])
S = coo_matrix(M)

这里,S.row 和 S.col 是行和列索引的数组,S.data 是这些索引处的值数组。所以你可以过滤

idx = S.data >= 0.8

例如,仅使用这些元素创建一个新矩阵:

S2 = coo_matrix((S.data[idx], (S.row[idx], S.col[idx])))
print S2

输出是

(0, 0)  1.0
(0, 2)  0.8
(1, 1)  1.0
(1, 2)  0.8
(2, 0)  0.8
(2, 1)  0.8
(2, 2)  1.0

注 (0,1) 不会出现,因为值为 0.7。

【讨论】:

【参考方案5】:

pandas 的read_table 可以处理sep 参数中的正则表达式。

In [19]: !head file.txt
sample  sample1 sample2 sample3
sample1 1   0.7 0.8
sample2 0.7 1   0.8
sample3 0.8 0.8 1

In [20]: df = pd.read_table('file.txt', sep='\s+')

In [21]: df
Out[21]:
    sample  sample1  sample2  sample3
0  sample1      1.0      0.7      0.8
1  sample2      0.7      1.0      0.8
2  sample3      0.8      0.8      1.0

从那里,您可以过滤所有 >= 0.8 的值。

In [23]: df[df >= 0.8]
Out[23]:
    sample  sample1  sample2  sample3
0  sample1      1.0      NaN      0.8
1  sample2      NaN      1.0      0.8
2  sample3      0.8      0.8      1.0

【讨论】:

我的问题是如何取出这些对?所需的输出是行名和列名。

以上是关于在 python 中读取矩阵并获取行名和列名的主要内容,如果未能解决你的问题,请参考以下文章

如何根据行名和列名的比较构建方阵

如何从数据框中检索行名和列名?

DataTable:如何使用行名和列名获取项目值? (五)

Pandas - 在应用映射期间检索每个元素的行名和列名

按行名过滤矩阵的嵌套列表

将 as.vector 应用于矩阵切片时保留列名