Search code examples
pythonmatplotlibargparse

python use argparse for passing optional arguments for plotting


I would like to pass optional arguments to a matplotlib script running from the command line.

I have the following:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')

import pandas as pd
import argparse

parser = argparse.ArgumentParser(description="Plot x,y data in python")
parser.add_argument('-xF',"--x", type=str, metavar='', required=True, help="x-Data")
parser.add_argument('-yF', "--y", type=str, metavar='', required=True, help="y-Data")
parser.add_argument('-o', "--options", nargs="+", default=[], help="Options for plotting")
args = parser.parse_args()

def plot(x, y, xlabel, ylabel, name, legend=None, otherArgs=None):

    xData = pd.read_csv(x) 
    yData = pd.read_csv(y)     

    plt.figure()
    plt.plot(xData, yData, label=legend, *(otherArgs), zorder=1)   
    plt.tight_layout()
    plt.savefig("tst.tiff")

if __name__ == '__main__':
    plot(args.x, args.y, args.options)

However, currently I am not able to do so. Is there any way to pass under the optional arguments other parameters to the plot functions? Set colors, markers, etc.. ?

Best Regards


Solution

  • If you will run as

    -o color=red marker=o
    

    then you will get list

    ["color=red", "marker=o"]
    

    and you can use split("=") and dict()

    args.options = dict(x.split('=') for x in args.options)
    

    to get it as dictionary

    {"color":"red", "marker":"o"}
    

    And then you can use ** to put dictionary

    plt.plot(..., **otherArgs)   
    

    and it will accept it as correct parameters.


    Minimal working code:

    For test I put argumenst directly in sys.argv - so I can run it without writing parameters in console.

    EDIT: it needs dictionary default={} instead of default=[]

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib
    #matplotlib.use('Agg')
    
    import pandas as pd
    import argparse
    
    # --- functions ---
    
    def plot(x, y, xlabel='X', ylabel='Y', name='Name', legend=None, otherArgs=None):
    
        xData = [1,2,3]
        yData = [3,1,1]
    
        plt.figure()
        plt.plot(xData, yData, label=legend, **otherArgs, zorder=1)   
        plt.tight_layout()
        #plt.savefig("tst.tiff")
        plt.show()
    
    # --- main ---
    
    if __name__ == '__main__':
        
        import sys
        sys.argv += ['--x', '1', '--y', '2', '-o', 'color=red', 'marker=o']
    
        parser = argparse.ArgumentParser(description="Plot x,y data in python")
        parser.add_argument('-xF',"--x", type=str, metavar='', required=True, help="x-Data")
        parser.add_argument('-yF', "--y", type=str, metavar='', required=True, help="y-Data")
        parser.add_argument('-o', "--options", nargs="+", default={}, help="Options for plotting")   # `default={}` instead of `default=[]`
        args = parser.parse_args()
    
        print('[args.options] before:', args.options)
        
        args.options = dict(x.split('=') for x in args.options)
    
        print('[args.options] after :', args.options)
        
        plot(args.x, args.y, otherArgs=args.options)
    

    Console:

    [args.options] before: ['color=green', 'marker=o']
    [args.options] after : {'color': 'green', 'marker': 'o'}
    

    EDIT:

    I found also method to create own class based on argparse.Action and later assign it to action in add_param(..., action=...)

    class keyvalue(argparse.Action):
        
        def __call__(self, parser, namespace, values, option_string):
            #print(parser)
            #print(values)
            #print(option_string)
            print('[namespace] before:', namespace)
            
            data = dict()
            
            for value in values:
                key, val = value.split('=')
                data[key] = val
                
            setattr(namespace, self.dest, data)
            
            print('[namespace] after :', namespace)
    
    parser.add_argument('-o', "--options", nargs="+", default={},  action=keyvalue)
    

    Full working code

    import argparse
    import matplotlib.pyplot as plt
    #matplotlib.use('Agg')
    #import pandas as pd
    
    # --- functions ---
    
    def plot(x, y, xlabel='X', ylabel='Y', name='Name', legend=None, otherArgs=None):
    
        xData = [1,2,3]
        yData = [3,1,1]
    
        plt.figure()
        plt.plot(xData, yData, label=legend, **otherArgs, zorder=1)   
        plt.tight_layout()
        #plt.savefig("tst.tiff")
        plt.show()
    
    class keyvalue(argparse.Action):
        
        def __call__(self, parser, namespace, values, option_string):
            #print(parser)
            #print(values)
            #print(option_string)
            print('[namespace] before:', namespace)
            
            data = dict()
            
            for value in values:
                key, val = value.split('=')
                data[key] = val
                
            setattr(namespace, self.dest, data)
            
            print('[namespace] after :', namespace)
    
    # --- main ---
            
    if __name__ == '__main__':
        
        import sys
        sys.argv += ['--x', '1', '--y', '2', '-o', 'color=green', 'marker=o']
    
        parser = argparse.ArgumentParser(description="Plot x,y data in python")
        parser.add_argument('-xF',"--x", type=str, metavar='', required=True, help="x-Data")
        parser.add_argument('-yF', "--y", type=str, metavar='', required=True, help="y-Data")
        parser.add_argument('-o', "--options", nargs="+", default={}, help="Options for plotting", action=keyvalue)
        args = parser.parse_args()
    
        print('[args.options] before:', args.options)
        
        plot(args.x, args.y, otherArgs=args.options)
    

    Source: