1

I have a matrix named t. I want to write it into a text file row by row. Also, each element in the matrix has a same format, for example 10.3f. The problem is that each row has 48 elements that I cannot write explicitly.

I tried to use {f}*{}:

for i in range(len(t[0][:])) :
    result.write( "'{8.3f}'*{per}\n".format( t[:][i],per=len(t[:][i]) ) )

I got the error as follow:

IndexError: tuple index out of range
martineau
  • 119,623
  • 25
  • 170
  • 301
fish_bu
  • 25
  • 4

3 Answers3

1

Although using [Scipy.Docs]: numpy.savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', footer='', comments='# ', encoding=None) would be the recommended way, it's also possible using Python formatting. Note that I assumed it's an NumPy array because of pieces of code like len(t[:][i]), but the code will work the same for a Python matrix as well.

Referencing [Python 3.Docs]: string - Format String Syntax.

There are 3 problems:

  • Missing a colon (":") in the float format specifier
  • "*" from inside the string format is just what it is, a string, and doesn't do what you want (multiplying the previous part). In order to do that, you must take it out of the format
  • The sequence argument must be unpacked ([Python 3.Docs]: More Control Flow Tools - Unpacking Argument Lists)

Example (I also cleaned your code a bit):

>>> import sys
>>> import numpy as np
>>>
>>> t = np.array([[1, 2, 3, 4], [5, 6, 7, 8, 9]])
>>>
>>> result = sys.stdout  # Use stdout to mimic file write
>>>
>>> for line in t:
...     result.write(("{:8.3f}" * len(line) + "\n").format(*line))
...
   1.000   2.000   3.000   4.000
33
   5.000   6.000   7.000   8.000   9.000
41

Note: The 2 unwanted lines in the output, are result.write's return value (number of written chars) which gets automatically printed.

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • This works perfect for me. Also I want to mention that using ( ("{:8.3f}" * "{per}" + "\n")).format(*t, per=len(t) ) causes a problem, because "{per}" return a string that cannot be multiplied. – fish_bu May 24 '19 at 21:03
  • That's true. You can't change the string format from itself. The format is final at the moment you are passing the variables to it. – CristiFati May 25 '19 at 09:25
1

I think you're on the right track—and here's how to make that work:

import sys

result = sys.stdout

t = [[3, 1.2, 3.45, 6.789],
     [863.202, 730.2, 833.67]]

for row in t:
    fmtspec = '{:8.3f}' * len(row)
    result.write(fmtspec.format(*row) + '\n')

Output:

   3.000   1.200   3.450   6.789
 863.202 730.200 833.670

If all the rows are the same length, you only need to create the formatting specification once:

t2 = [[3, 1.2, 3.45, 6.789],
     [863.202, 730.2, 833.67, 23.1456]]

fmtspec = '{:8.3f}' * len(t2[0])  # Use length of first row.
for row in t2:
    result.write(fmtspec.format(*row) + '\n')
   3.000   1.200   3.450   6.789
 863.202 730.200 833.670  23.146
martineau
  • 119,623
  • 25
  • 170
  • 301
0

I would suggest DictWriter if applicable.

Other option would be similar to previously given:

import os
FORMAT = "{:10.3f}"

t = [ [123, 1.2, 3.45, 6.789],
    [3863.202, 730.2, 833.67],
    [863.202, 7230.2, 833.67], ]

def numbers_to_line(*args):
    template = FORMAT * len(args) + os.linesep
    return template.format(*args)

with open("result.txt", "w") as fh:
    for row in t:
        fh.write(numbers_to_line(*row))

and the output:

   123.000     1.200     3.450     6.789
  3863.202   730.200   833.670
   863.202  7230.200   833.670