Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matlab's vectorized sprintf like function in python

After using Matlab for some time I grew quite fond of its sprintf function, which is vectorized (vectorization is the crucial part of the question).

Assuming one has a listli=[1,2,3,4,5,6],

sprintf("%d %d %d\n", li)

would apply the format on the elements in li one after another returning

"1 2 3\n4 5 6\n" 

as string.

My current solution does not strike as very pythonic:

def my_sprintf(formatstr, args):

    #number of arguments for format string:
    n=formatstr.count('%')

    res=""

    #if there are k*n+m elements in the list, leave the last m out
    for i in range(n,len(args)+1,n):
        res+=formatstr%tuple(args[i-n:i])

    return res

What would be the usual/better way of doing it in python?

Would it be possible, without explicitly eliciting the number of expected parameters from the format string (n=formatstr.count('%') feels like a hack)?

PS: For the sake of simplicity one could assume, that the number of elements in the list is a multiple of number of arguments in the format string.

like image 630
ead Avatar asked Sep 18 '25 03:09

ead


1 Answers

You could use a variation of the grouper recipe if you get the user to pass in the chunk size.

def sprintf(iterable,fmt, n):
    args = zip(*[iter(iterable)] * n)
    return "".join([fmt % t for t in args])

Output:

In [144]: sprintf(li,"%.2f %.2f %d\n", 3)
Out[144]: '1.00 2.00 3\n4.00 5.00 6\n'

In [145]: sprintf(li,"%d %d %d\n", 3)
Out[145]: '1 2 3\n4 5 6\n'

You could handle when the chunk size was not a multiple of the list size using izip_longest and str.format but it would not let you specify the types without erroring :

from itertools import izip_longest


def sprintf(iterable, fmt, n, fillvalue=""):
    args = izip_longest(*[iter(iterable)] * n, fillvalue=fillvalue)
    return "".join([fmt.format(*t) for t in args])

If you split the placeholders or get the user to pass an iterable of placeholders you could catch all the potential issues.

 def sprintf(iterable, fmt, sep=" "):
    obj = object()
    args = izip_longest(*[iter(iterable)] * len(fmt), fillvalue=obj)
    return "".join(["{sep}".join([f % i for f, i in zip(fmt, t) if i is not obj]).format(sep=sep) + "\n"
                    for t in args])

Demo:

In [165]: sprintf(li, ["%.2f", "%d", "%.2f", "%2.f"])
Out[165]: '1.00 2 3.00  4\n5.00 6\n'

In [166]: sprintf(li, ["%d", "%d", "%d"])
Out[166]: '1 2 3\n4 5 6\n'

In [167]: sprintf(li, ["%f", "%f", "%.4f"])
Out[167]: '1.000000 2.000000 3.0000\n4.000000 5.000000 6.0000\n'

In [168]: sprintf(li, ["%.2f", "%d", "%.2f", "%2.f"])
Out[168]: '1.00 2 3.00  4\n5.00 6\n'
like image 102
Padraic Cunningham Avatar answered Sep 19 '25 19:09

Padraic Cunningham