在NumPy数组中添加行/列标题

32

我有一个NumPy ndarray,我想添加行/列标题。

实际上,数据是7x12x12,但是我可以像这样表示:

  A=[[[0, 1, 2, 3, 4, 5],
      [1, 0, 3, 4, 5, 6],
      [2, 3, 0, 5, 6, 7],
      [3, 4, 5, 0, 7, 8],
      [4, 5, 6, 7, 0, 9],
      [5, 6, 7, 8, 9, 0]]


     [[0, 1, 2, 3, 4, 5],
      [1, 0, 3, 4, 5, 6],
      [2, 3, 0, 5, 6, 7],
      [3, 4, 5, 0, 7, 8],
      [4, 5, 6, 7, 0, 9],
      [5, 6, 7, 8, 9, 0]]]

假设A是我的2x6x6数组。

我如何跨越第一行和第一列插入标题,以便每个数组在我的CSV输出文件中看起来像这样?

        A, a, b, c, d, e, f 
        a, 0, 1, 2, 3, 4, 5,
        b, 1, 0, 3, 4, 5, 6,
        c, 2, 3, 0, 5, 6, 7,
        d, 3, 4, 5, 0, 7, 8,
        e, 4, 5, 6, 7, 0, 9,
        f, 5, 6, 7, 8, 9, 0

我所做的是将数组设置为7x13x13,并插入数据,以便我有一行和一列的零,但我更喜欢字符串。

我想我可以编写一个Excel宏来将零替换为字符串。然而,问题在于如果我尝试将这些零重新分配为我想要的字符串,NumPy无法将string转换为float


我不熟悉numpy,但如果它们只是列表,这将非常简单。那样的解决方案是否可行?你能把numpy矩阵转换成列表吗? - Paul Seeb
NumPy矩阵的工作方式是它们可以作为列表的列表,因此您可以单独对它们进行迭代。 - JAB
5个回答

45

使用pandas.DataFrame.to_csv函数,可以将数据的列和索引写入文件:

import numpy as np
import pandas as pd

A = np.random.randint(0, 10, size=36).reshape(6, 6)
names = [_ for _ in 'abcdef']
df = pd.DataFrame(A, index=names, columns=names)
df.to_csv('df.csv', index=True, header=True, sep=' ')

这将为您提供以下df.csv文件:

  a b c d e f 
a 1 5 5 0 4 4 
b 2 7 5 4 0 9 
c 6 5 6 9 7 0 
d 4 3 7 9 9 3 
e 8 1 5 1 9 0 
f 2 8 0 0 5 1    

20

Numpy 可以很好地处理 n 维数组,但其中许多功能仅限于二维数组。甚至不确定您希望输出文件的外观。

许多希望拥有命名列的人忽视了 Numpy 的 recarray() 功能。这是要了解的好东西,但它只“命名”了一个维度。

对于二维数组,Pandas 非常实用。

In [275]: DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
   .....:                      orient='index', columns=['one', 'two', 'three'])
Out[275]: 
   one  two  three
A    1    2      3
B    4    5      6
如果您只是想解决输出问题,我建议您只使用几行手写代码,这比为了一个功能而安装另一个包要更加轻量级。

3

Think this does the trick generically

Input

mats = array([[[0, 1, 2, 3, 4, 5],
    [1, 0, 3, 4, 5, 6],
    [2, 3, 0, 5, 6, 7],
    [3, 4, 5, 0, 7, 8],
    [4, 5, 6, 7, 0, 9],
    [5, 6, 7, 8, 9, 0]],

   [[0, 1, 2, 3, 4, 5],
    [1, 0, 3, 4, 5, 6],
    [2, 3, 0, 5, 6, 7],
    [3, 4, 5, 0, 7, 8],
    [4, 5, 6, 7, 0, 9],
    [5, 6, 7, 8, 9, 0]]])

代码

# Recursively makes pyramiding column and row headers
def make_head(n):
    pre = ''
    if n/26:
        pre = make_head(n/26-1)

    alph = "abcdefghijklmnopqrstuvwxyz"
    pre+= alph[n%26]
    return pre

# Generator object to create header items for n-rows or n-cols
def gen_header(nitems):
    n = -1
    while n<nitems:
        n+=1
        yield make_head(n)

# Convert numpy to list
lmats = mats.tolist()

# Loop through each "matrix"
for mat in lmats:
    # Pre store number of columns as we modify it before working rows
    ncols = len(mat[0])

    # add header value to front of each row from generator object
    for row,hd in zip(mat,gen_header(len(mat))):
        row.insert(0,hd)

    # Create a "header" line for all the columns
    col_hd = [hd for hd in gen_header(ncols-1)]
    col_hd.insert(0,"A")

    # Insert header line into lead row of matrix
    mat.insert(0,col_hd)

# Convert back to numpy
mats = numpy.array(lmats)

输出(存储在mats中的值):

array([[['A', 'a', 'b', 'c', 'd', 'e', 'f'],
        ['a', '0', '1', '2', '3', '4', '5'],
        ['b', '1', '0', '3', '4', '5', '6'],
        ['c', '2', '3', '0', '5', '6', '7'],
        ['d', '3', '4', '5', '0', '7', '8'],
        ['e', '4', '5', '6', '7', '0', '9'],
        ['f', '5', '6', '7', '8', '9', '0']],

       [['A', 'a', 'b', 'c', 'd', 'e', 'f'],
        ['a', '0', '1', '2', '3', '4', '5'],
        ['b', '1', '0', '3', '4', '5', '6'],
        ['c', '2', '3', '0', '5', '6', '7'],
        ['d', '3', '4', '5', '0', '7', '8'],
        ['e', '4', '5', '6', '7', '0', '9'],
        ['f', '5', '6', '7', '8', '9', '0']]], 
      dtype='|S4')

我遇到了一个错误:'numpy.ndarray' object has no attribute 'insert'。有什么解决方法建议吗? - emmagras
解决方法已经包含在里面了。我将numpy矩阵转换为列表,进行操作后再转换回去。Numpy的插入例程要么非常愚蠢,要么我没有看到它们的用处。 - Paul Seeb
谢谢。我最终通过这个找到了答案。 - emmagras

2

我不知道有什么方法可以添加矩阵头部(即使我觉得这很有用)。我的做法是创建一个小类,重载 __str__ 函数打印对象。

像这样:

class myMat:
    def __init__(self, mat, name):
        self.mat = mat
        self.name = name
        self.head = ['a','b','c','d','e','f']
        self.sep = ','

    def __str__(self):
        s = "%s%s"%(self.name,self.sep)
        for x in self.head:
            s += "%s%s"%(x,self.sep)
        s = s[:-len(self.sep)] + '\n'

        for i in range(len(self.mat)):
            row = self.mat[i]
            s += "%s%s"%(self.head[i],self.sep)
            for x in row:
                s += "%s%s"%(str(x),self.sep)
            s += '\n'
        s = s[:-len(self.sep)-len('\n')]

        return s

你可以轻松地打印带标题的内容,使用以下代码:

print myMat(A,'A')
print myMat(B,'B')

这看起来很有前途。在试图概括我的问题时,我把事情搞混了,因为大矩阵实际上并不是由标记的小矩阵组成的。我尝试将其拆分并实现您的建议,但它没有起作用。首先,在此行中s +=“%s%s”%(self.head [i],self.sep)处出现“列表索引超出范围”的错误。如果A是唯一的矩阵,而不是处理编译矩阵的矩阵,您的建议会如何改变? - emmagras
我猜你因为矩阵大小不同而得到了一个索引超出范围的错误。现在这段代码只能用于6x6的矩阵(即len(['a','b','c','d','e','f']))。只需更改定义self.head的行以适应您的矩阵大小即可(例如,如果您的矩阵是3x3,则该行应如下所示:self.head=['a','b','c'])。希望这可以帮助您! - Oriol Nieto

2

我不是很确定,但你可以考虑看一下Pandas


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接