Tohiko
Tohiko

Reputation: 1982

ArgumentParser for numpy array

Is there a way to add an argument to ArgumentParser for an np.array instead of a list? I know I can do something like this

import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('-foo', action='store', type=int, nargs='+')
args = parser.parse_args(['-foo', '1', '2'])
args.foo = np.array(args.foo)

But I would like to specify the complete description of the arguments before parsing.

Is there a way to do this?

Upvotes: 2

Views: 4654

Answers (2)

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21453

To specify a slight change in how the StoreAction action handler works you would create a subclass of the handler with appropriate change (the docs have an example right above this section)

import argparse, numpy as np

class Store_as_array(argparse._StoreAction):
    def __call__(self, parser, namespace, values, option_string=None):
        values = np.array(values)
        return super().__call__(parser, namespace, values, option_string)

parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('-foo', action=Store_as_array, type=int, nargs='+')
                                  # ^ specify as the action 
args = parser.parse_args(['-foo', '1', '2'])

assert isinstance(args.foo, np.ndarray)

Upvotes: 4

hpaulj
hpaulj

Reputation: 231510

As indicated in my comment, 'complete description before parsing' is unclear.

But it occurred to me that it is possible to create a 2d array with argparse. I can use nargs=3 to specify 3 columns, and action='append' to collect the input in multiple sublists. And of course type specifies whether it int or float.

In [27]: p=argparse.ArgumentParser()

In [28]: p.add_argument('-a',action='append',nargs='+',type=int)
Out[28]: _AppendAction(option_strings=['-a'], dest='a', nargs='+', const=None, default=None, type=<class 'int'>, choices=None, help=None, metavar=None)

In [29]: args=p.parse_args('-a 1 2 3 -a 4 5 6 -a 7 8 9'.split())

In [30]: args
Out[30]: Namespace(a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]])

In [31]: np.array(args.a)
Out[31]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

It will complain if I don't have the right number of columns. All it doesn't control is the number of rows; but I can easily check the shape of the array after creation.

With this append it doesn't make sense to create the array in the action. And even with the 1d input, there isn't much of an advantage to performing that np.array call during parsing. Massaging (and testing) the args values after parsing is perfectly good practice.

It would better, though to put the array values in a CSV file, and specify the name of that file via argparse. argparse really isn't meant for input of a large number of values. It's not a general purpose file parser. The command line controls the behavior of your code.

Upvotes: 2

Related Questions