Como escrever uma matriz multidimensional em um arquivo de texto?

115

Em outra pergunta, outros usuários ofereceram ajuda se eu pudesse fornecer o array com o qual estava tendo problemas. No entanto, eu até falho em uma tarefa básica de E / S, como gravar um array em um arquivo.

Alguém pode explicar que tipo de loop eu precisaria escrever um array numpy 4x11x14 para o arquivo?

Este array consiste em quatro arrays 11 x 14, então devo formatá-lo com uma nova linha, para tornar a leitura do arquivo mais fácil para os outros.

Edit : Então, tentei a função numpy.savetxt. Estranhamente, dá o seguinte erro:

TypeError: float argument required, not numpy.ndarray

Presumo que seja porque a função não funciona com matrizes multidimensionais. Alguma solução como eu gostaria dentro de um arquivo?

Ivo Flipse
fonte

Respostas:

197

Se você quiser gravá-lo no disco de forma que seja fácil lê-lo de volta como uma matriz numpy, examine numpy.save. A decapagem também funcionará bem, mas é menos eficiente para matrizes grandes (o que não é o seu, portanto, qualquer um deles está perfeitamente bem).

Se você quiser que seja legível por humanos, dê uma olhada numpy.savetxt.

Edit: Então, parece que savetxtnão é uma opção tão boa para arrays com> 2 dimensões ... Mas apenas para tirar tudo à sua conclusão completa:

Acabei de perceber que numpy.savetxt engasga com ndarrays com mais de 2 dimensões ... Isso provavelmente ocorre por design, pois não há uma maneira inerentemente definida de indicar dimensões adicionais em um arquivo de texto.

Por exemplo, isto (uma matriz 2D) funciona bem

import numpy as np
x = np.arange(20).reshape((4,5))
np.savetxt('test.txt', x)

Embora a mesma coisa falhe (com um erro bastante pouco informativo:) TypeError: float argument required, not numpy.ndarraypara uma matriz 3D:

import numpy as np
x = np.arange(200).reshape((4,5,10))
np.savetxt('test.txt', x)

Uma solução alternativa é dividir a matriz 3D (ou superior) em fatias 2D. Por exemplo

x = np.arange(200).reshape((4,5,10))
with file('test.txt', 'w') as outfile:
    for slice_2d in x:
        np.savetxt(outfile, slice_2d)

No entanto, nosso objetivo é ser claramente legível por humanos, ao mesmo tempo que pode ser lido facilmente com numpy.loadtxt. Portanto, podemos ser um pouco mais detalhados e diferenciar as fatias usando linhas comentadas. Por padrão, numpy.loadtxtirá ignorar todas as linhas que começam com #(ou qualquer caractere especificado pelo commentskwarg). (Parece mais prolixo do que realmente é ...)

import numpy as np

# Generate some test data
data = np.arange(200).reshape((4,5,10))

# Write the array to disk
with open('test.txt', 'w') as outfile:
    # I'm writing a header here just for the sake of readability
    # Any line starting with "#" will be ignored by numpy.loadtxt
    outfile.write('# Array shape: {0}\n'.format(data.shape))

    # Iterating through a ndimensional array produces slices along
    # the last axis. This is equivalent to data[i,:,:] in this case
    for data_slice in data:

        # The formatting string indicates that I'm writing out
        # the values in left-justified columns 7 characters in width
        # with 2 decimal places.  
        np.savetxt(outfile, data_slice, fmt='%-7.2f')

        # Writing out a break to indicate different slices...
        outfile.write('# New slice\n')

Isso produz:

# Array shape: (4, 5, 10)
0.00    1.00    2.00    3.00    4.00    5.00    6.00    7.00    8.00    9.00   
10.00   11.00   12.00   13.00   14.00   15.00   16.00   17.00   18.00   19.00  
20.00   21.00   22.00   23.00   24.00   25.00   26.00   27.00   28.00   29.00  
30.00   31.00   32.00   33.00   34.00   35.00   36.00   37.00   38.00   39.00  
40.00   41.00   42.00   43.00   44.00   45.00   46.00   47.00   48.00   49.00  
# New slice
50.00   51.00   52.00   53.00   54.00   55.00   56.00   57.00   58.00   59.00  
60.00   61.00   62.00   63.00   64.00   65.00   66.00   67.00   68.00   69.00  
70.00   71.00   72.00   73.00   74.00   75.00   76.00   77.00   78.00   79.00  
80.00   81.00   82.00   83.00   84.00   85.00   86.00   87.00   88.00   89.00  
90.00   91.00   92.00   93.00   94.00   95.00   96.00   97.00   98.00   99.00  
# New slice
100.00  101.00  102.00  103.00  104.00  105.00  106.00  107.00  108.00  109.00 
110.00  111.00  112.00  113.00  114.00  115.00  116.00  117.00  118.00  119.00 
120.00  121.00  122.00  123.00  124.00  125.00  126.00  127.00  128.00  129.00 
130.00  131.00  132.00  133.00  134.00  135.00  136.00  137.00  138.00  139.00 
140.00  141.00  142.00  143.00  144.00  145.00  146.00  147.00  148.00  149.00 
# New slice
150.00  151.00  152.00  153.00  154.00  155.00  156.00  157.00  158.00  159.00 
160.00  161.00  162.00  163.00  164.00  165.00  166.00  167.00  168.00  169.00 
170.00  171.00  172.00  173.00  174.00  175.00  176.00  177.00  178.00  179.00 
180.00  181.00  182.00  183.00  184.00  185.00  186.00  187.00  188.00  189.00 
190.00  191.00  192.00  193.00  194.00  195.00  196.00  197.00  198.00  199.00 
# New slice

A leitura de volta é muito fácil, desde que saibamos a forma do array original. Nós podemos apenas fazer numpy.loadtxt('test.txt').reshape((4,5,10)). Por exemplo (você pode fazer isso em uma linha, estou apenas sendo prolixo para esclarecer as coisas):

# Read the array from disk
new_data = np.loadtxt('test.txt')

# Note that this returned a 2D array!
print new_data.shape

# However, going back to 3D is easy if we know the 
# original shape of the array
new_data = new_data.reshape((4,5,10))

# Just to check that they're the same...
assert np.all(new_data == data)
Joe Kington
fonte
2
+1 de mim, veja também numpy.loadtxt( docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html )
Dominic Rodger
2
Há uma solução muito mais fácil agora para este problema aqui: yourStrArray = np.array ([str (val) para val em yourMulDArray], dtype = 'string'); np.savetxt ('YourTextFile.txt', yourStrArray, fmt = '% s')
Greg Kramida
@GregKramida e como você recupera o array?
astrojuanlu
@ Juanlu001: Eu sei que numpy.loadtxt (...) também aceita um argumento dtype, que pode ser definido como np.string_. Eu daria uma chance, primeiro e imediatamente. Também existe um numpy.fromstring (...) para analisar matrizes de strings.
Greg Kramida
Ei, e se eu precisar armazenar uma matriz de imagens? Como podemos redimensionar isso se o tamanho da imagem for, digamos, 512 x 512?
Ambika Saxena
31

Não tenho certeza se isso atende aos seus requisitos, visto que acho que você está interessado em tornar o arquivo legível para as pessoas, mas se essa não for a principal preocupação, apenas pickleisso.

Para salvá-lo:

import pickle

my_data = {'a': [1, 2.0, 3, 4+6j],
           'b': ('string', u'Unicode string'),
           'c': None}
output = open('data.pkl', 'wb')
pickle.dump(my_data, output)
output.close()

Para ler de volta:

import pprint, pickle

pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)
pprint.pprint(data1)

pkl_file.close()
Dominic Rodger
fonte
Talvez você não precise pprintimprimir o dicionário.
zyy
11

Se você não precisa de uma saída legível por humanos, outra opção que você pode tentar é salvar o array como um .matarquivo MATLAB , que é um array estruturado. Eu desprezo o MATLAB, mas o fato de poder ler e escrever .matem poucas linhas é conveniente.

Ao contrário da resposta de Joe Kington, o benefício disso é que você não precisa saber a forma original dos dados no .matarquivo, ou seja, não há necessidade de remodelar ao ler. E, ao contrário do uso pickle, um .matarquivo pode ser lido pelo MATLAB, e provavelmente alguns outros programas / linguagens também.

Aqui está um exemplo:

import numpy as np
import scipy.io

# Some test data
x = np.arange(200).reshape((4,5,10))

# Specify the filename of the .mat file
matfile = 'test_mat.mat'

# Write the array to the mat file. For this to work, the array must be the value
# corresponding to a key name of your choice in a dictionary
scipy.io.savemat(matfile, mdict={'out': x}, oned_as='row')

# For the above line, I specified the kwarg oned_as since python (2.7 with 
# numpy 1.6.1) throws a FutureWarning.  Here, this isn't really necessary 
# since oned_as is a kwarg for dealing with 1-D arrays.

# Now load in the data from the .mat that was just saved
matdata = scipy.io.loadmat(matfile)

# And just to check if the data is the same:
assert np.all(x == matdata['out'])

Se você esquecer a chave de nome da matriz no .matarquivo, você sempre pode fazer:

print matdata.keys()

E, claro, você pode armazenar muitos arrays usando muito mais chaves.

Então, sim - não será legível com seus olhos, mas leva apenas 2 linhas para escrever e ler os dados, o que eu acho que é uma troca justa.

Dê uma olhada nos documentos de scipy.io.savemat e scipy.io.loadmat e também nesta página de tutorial: scipy.io File IO Tutorial

aseagrama
fonte
9

ndarray.tofile() também deve funcionar

por exemplo, se seu array é chamado a:

a.tofile('yourfile.txt',sep=" ",format="%s")

Não tenho certeza de como obter a formatação de nova linha.

Editar (credite o comentário de Kevin J. Black aqui ):

Desde a versão 1.5.0, np.tofile()usa um parâmetro opcional newline='\n'para permitir a saída de várias linhas. https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.savetxt.html

atomh33ls
fonte
Mas existe uma maneira de criar um array original a partir do arquivo tex?
Ahashan Alam Sojib
@AhashanAlamSojib consulte stackoverflow.com/questions/3518778/…
atomh33ls
tofilenão tem newline='\n'.
Nico Schlömer
1

Você pode simplesmente percorrer a matriz em três loops aninhados e gravar seus valores em seu arquivo. Para leitura, você simplesmente usa a mesma construção de loop exata. Você obterá os valores exatamente na ordem certa para preencher seus arrays corretamente novamente.

Jwueller
fonte
0

Eu tenho uma maneira de fazer isso usando simplesmente uma operação filename.write (). Funciona bem para mim, mas estou lidando com matrizes com aproximadamente 1.500 elementos de dados.

Basicamente, tenho apenas loops for para iterar pelo arquivo e gravá-lo no destino de saída linha por linha em uma saída de estilo csv.

import numpy as np

trial = np.genfromtxt("/extension/file.txt", dtype = str, delimiter = ",")

with open("/extension/file.txt", "w") as f:
    for x in xrange(len(trial[:,1])):
        for y in range(num_of_columns):
            if y < num_of_columns-2:
                f.write(trial[x][y] + ",")
            elif y == num_of_columns-1:
                f.write(trial[x][y])
        f.write("\n")

As instruções if e elif são usadas para adicionar vírgulas entre os elementos de dados. Por alguma razão, eles são eliminados ao ler o arquivo como um array nd. Meu objetivo era gerar o arquivo como um csv, então esse método ajuda a lidar com isso.

Espero que isto ajude!

BennyD
fonte
0

Pickle é o melhor para esses casos. Suponha que você tenha um ndarray chamado x_train. Você pode despejá-lo em um arquivo e revertê-lo usando o seguinte comando:

import pickle

###Load into file
with open("myfile.pkl","wb") as f:
    pickle.dump(x_train,f)

###Extract from file
with open("myfile.pkl","rb") as f:
    x_temp = pickle.load(f)
Kenpachi Zaraki
fonte