Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for multi formatting elements in savetxt (Trac #663) #1261

Closed
numpy-gitbot opened this issue Oct 19, 2012 · 4 comments
Closed

Support for multi formatting elements in savetxt (Trac #663) #1261

numpy-gitbot opened this issue Oct 19, 2012 · 4 comments

Comments

@numpy-gitbot
Copy link

Original ticket http://projects.scipy.org/numpy/ticket/663 on 2008-02-21 by @huard, assigned to unknown.

Here is a patch to add support for multiple formats in savetxt.

Index: numpy/lib/io.py
===================================================================
--- numpy/lib/io.py     (revision 4815)
+++ numpy/lib/io.py     (working copy)
@@ -326,6 +326,10 @@
     the file is automatically saved in compressed gzip format.  The load()
     command understands gzipped files transparently.

+    fmt can be a single format (%10.5f), a sequence of formats, or a 
+    single multi-format string, e.g. 'Iteration %d -- %10.5f', in which
+    case delimiter is ignored.
+    
     Example usage:

     save('test.out', X)         # X is an array
@@ -354,8 +358,23 @@
     if len(X.shape)==1:
         origShape = X.shape
         X.shape = len(X), 1
+    
+    # Fmt can be a string with multiple insertion points or a list of formats.
+    # E.g. '%10.5f\t%10d' or ('%10.5f', '$10d')
+    if type(fmt) in (list, tuple):
+        if len(fmt) != X.shape[1]:
+            raise AttributeError, 'fmt has wrong shape.  '+ str(fmt)
+        format = delimiter.join(fmt)
+    elif type(fmt) is str:
+        if fmt.count('%') == 1:
+            fmt = [fmt,]*X.shape[1]
+            format = delimiter.join(fmt)
+        elif fmt.count('%') != X.shape[1]:
+            raise AttributeError, 'fmt has wrong number of % formats.  ' + fmt
+        else:
+            format = fmt
     for row in X:
-        fh.write(delimiter.join([fmt%val for val in row]) + '\n')
+        fh.write(format%tuple(row) + '\n')

     if origShape is not None:
         X.shape = origShape
@numpy-gitbot
Copy link
Author

@huard wrote on 2008-04-09

Let me add some tests, it's going to be easier to review.

@numpy-gitbot
Copy link
Author

Attachment added by @huard on 2008-04-09: io.patch

@numpy-gitbot
Copy link
Author

@huard wrote on 2008-04-16

To increase the probability of someone reviewing this, I tried to simplify the process. I committed the part of the patch related to docstring enhancements as well as fixes and additions to the tests (r5037). The test for multi-format is simply commented out in the test_io.py file. Here is the slimmed version of the patch for multi-format support in io.py. Simply uncomment the tests related to format in test_io.py to make sure everything works as advertised.

Index: io.py
===================================================================
--- io.py       (revision 5037)
+++ io.py       (working copy)
@@ -316,7 +316,6 @@
     else:  return X


-# adjust so that fmt can change across columns if desired.

 def savetxt(fname, X, fmt='%.18e',delimiter=' '):
     """
@@ -331,9 +330,9 @@
       transparently.
     X : array or sequence
       Data to write to file.
-    fmt : string 
-      A format string %[flags][width][.precision]specifier. See notes below for 
-      a description of some common flags and specifiers.
+    fmt : format string or sequence of format strings 
+      A single format (%10.5f), a sequence of formats, or a multi-format string, 
+      e.g. 'Iteration %d -- %10.5f', in which case delimiter is ignored.
     delimiter : str
       Character separating columns.

@@ -382,18 +381,34 @@
     else:
         raise ValueError('fname must be a string or file handle')

+    X = np.asarray(X)
+    if X.ndim == 1:
+        if X.dtype.names is None:
+            X = np.atleast_2d(X).T
+            ncol = 1
+        else:
+            ncol = len(X.dtype.descr)
+    else:
+        ncol = X.shape[1]
+        
+    # Fmt can be a string with multiple insertion points or a list of formats.
+    # E.g. '%10.5f\t%10d' or ('%10.5f', '$10d')
+    if type(fmt) in (list, tuple):
+        if len(fmt) != ncol:
+            raise AttributeError, 'fmt has wrong shape.  '+ str(fmt)
+        format = delimiter.join(fmt)
+    elif type(fmt) is str:
+        if fmt.count('%') == 1:
+            fmt = [fmt,]*ncol
+            format = delimiter.join(fmt)
+        elif fmt.count('%') != ncol:
+            raise AttributeError, 'fmt has wrong number of % formats.  ' + fmt
+        else:
+            format = fmt

-    X = np.asarray(X)
-    origShape = None
-    if len(X.shape)==1 and X.dtype.names is None:
-        origShape = X.shape
-        X.shape = len(X), 1
     for row in X:
-        fh.write(delimiter.join([fmt%val for val in row]) + '\n')
+        fh.write(format%tuple(row) + '\n')

-    if origShape is not None:
-        X.shape = origShape
-
 import re
 def fromregex(file, regexp, dtype):
     """Construct a record array from a text file, using regular-expressions parsing.

@numpy-gitbot
Copy link
Author

@stefanv wrote on 2008-04-28

Thank you, David. Applied in r5108.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant