0

For my research I want to be able to quickly produce multiple graphs of a particular kind, but with slightly different data (e.g. different dates or different sensors). I'm trying to write a function that produces a graph using a few mandatory arguments and up to 20 optional arguments. I want this function to: 1) be able to produce a nice graph when I give it just one sensor as well as when I give it 10 sensors. 2) Show only the desired time between starttime and endtime
The code I have so far is:

import numpy as np
import pvlib as pvl
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

def temp1_multiple_days(startdatetime, enddatetime, index, temp1, label1, windowtitle = 'Temperatures over time'):
    d = {'index':index, 'temp1': temp1}
    frame = pd.DataFrame(data = d)
    frame.set_index([index], inplace = True)
    frame = frame[startdatetime:enddatetime]  #slicing right dates out of large dataset
    fig, ax1 = plt.subplots()
    fig.canvas.set_window_title(windowtitle) 
    ax1.plot(frame.index, frame.temp1, label = label1)
    ax1.xaxis.set_major_formatter(mdates.DateFormatter("%d-%b-'%y"))
    ax1.xaxis.set_major_locator(mdates.DayLocator())
    ax1.set_xlim(startdatetime, enddatetime)
    ax1.set_ylabel('Temperature (°C)')
    ax1.legend(loc=1)
    fig.tight_layout
    fig.autofmt_xdate()
    plt.grid(True)
    plt.show

This produces the desired results if I give it 1 sensor. For more sensors, I create a new function. So now I have defined 10 functions, with this being number 10:

def temp10_multiple_days(startdatetime, enddatetime, index, temp1, label1, temp2, label2, temp3, label3, temp4, label4, temp5, label5, temp6, label6, temp7, label7, temp8, label8, temp9, label9, temp10, label10, windowtitle = 'Temperatures over time'):
    d = {'index':index, 'temp1': temp1, 'temp2': temp2, 'temp3': temp3, 'temp4': temp4, 'temp5': temp5, 'temp6': temp6, 'temp7': temp7, 'temp8': temp8, 'temp9': temp9, 'temp10': temp10}
    frame = pd.DataFrame(data = d)
    frame.set_index([index], inplace = True)
    frame = frame[startdatetime:enddatetime]    #slicing right dates out of large dataset
    fig, ax1 = plt.subplots()
    fig.canvas.set_window_title(windowtitle) 
    ax1.plot(frame.index, frame.temp1, label = label1)
    ax1.plot(frame.index, frame.temp2, label = label2)
    ax1.plot(frame.index, frame.temp3, label = label3)
    ax1.plot(frame.index, frame.temp4, label = label4)
    ax1.plot(frame.index, frame.temp5, label = label5)
    ax1.plot(frame.index, frame.temp6, label = label6)
    ax1.plot(frame.index, frame.temp7, label = label7)
    ax1.plot(frame.index, frame.temp8, label = label8)
    ax1.plot(frame.index, frame.temp9, label = label9)
    ax1.plot(frame.index, frame.temp10, label = label10)
    ax1.xaxis.set_major_formatter(mdates.DateFormatter("%d-%b-'%y"))
    ax1.xaxis.set_major_locator(mdates.DayLocator())
    ax1.set_xlim(startdatetime, enddatetime)
    ax1.set_ylabel('Temperature (°C)')
    ax1.legend(loc=1)
    fig.tight_layout
    fig.autofmt_xdate()
    plt.grid(True)
    plt.show

Now my question is: How do I make this into one function that can take 20 or more optional arguments?

nekomatic
  • 5,988
  • 1
  • 20
  • 27
Hans
  • 9
  • 5
  • 1
    Possible duplicate of [What does \*\* (double star/asterisk) and \* (star/asterisk) do for parameters?](https://stackoverflow.com/questions/36901/what-does-double-star-asterisk-and-star-asterisk-do-for-parameters) – DavidG May 01 '18 at 14:05
  • 1
    Possible duplicate of [Multiple optional arguments python](https://stackoverflow.com/questions/43279256/multiple-optional-arguments-python) – palivek May 01 '18 at 14:08

3 Answers3

1

Probably the easiest way is to just pass a list of temp-label pairs.

def temp10_multiple_days(startdatetime, enddatetime, index, temp_label_pairs,
                         windowtitle = 'Temperatures over time'):

    for (temp, label) in temp_label_pairs:
        ax.plot(frame.index, temp, label)
BallpointBen
  • 9,406
  • 1
  • 32
  • 62
  • I'm trying to get this to work, but I can't figure out how to format it so that the slicing of the local dataframe happens correctly. – Hans May 01 '18 at 14:27
0

You could use an *args parameter (you can call it *temps or whatever - the * is the important part). A parameter with * accepts an arbitrary number of positional arguments and you can loop over them as a list. Since you also need a label for each, you could expect each parameter to be a tuple (value, "label").

For example, within your function, instead of having to define a function for each possible amount of parameters:

def multiple_days(startdatetime, enddatetime, index, *temps, windowtitle = "Temperatures over time'):
    # ...
    for temp in temps:
        ax1.plot(frame.index, frame.temp[0], label = temp[1])
    # ...

Or you could of course do the same with a keyword argument **temps accepting a list of tuples, if you want to call the function by explicitly specifying where the temps are given.

theberzi
  • 2,142
  • 3
  • 20
  • 34
  • I don't have much experience with pandas or matplotlib yet, but I've found this, I wonder if it solves your problem: https://stackoverflow.com/questions/16175874/python-pandas-dataframe-slicing-by-date-conditions – theberzi May 02 '18 at 09:44
0

I found the answer to the slicing/truncate problem. I input the whole dataframe (which has all sensors) into the function. The whole frame is then sliced locally. Columns with sensors are then retrieved as a string.

def temp_multiple_days(df, startdatetime, enddatetime, *temps, windowtitle = 'Temperatures over time'):
df = df.truncate(startdatetime, enddatetime)
fig, ax1 = plt.subplots()
fig.canvas.set_window_title(windowtitle) 
for (temp, label) in temps:
    ax1.plot(df.index, df[temp], label = label)
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%d-%b-'%y"))
ax1.xaxis.set_major_locator(mdates.DayLocator())
ax1.set_xlim(startdatetime, enddatetime)
ax1.set_ylabel('Temperature (°C)')
ax1.legend(loc=1)
fig.tight_layout
fig.autofmt_xdate()
plt.grid(True)
plt.show     
# i then call to the function in this way:
temp_multiple_days(df, '2018-04-21 00:00:00', '2018-04-27 23:59:59', ('name of sensor 1', 'graph label 1'), ('name of sensor 2', 'graph label 2'), windowtitle= 'A nice title')

Thanks for the help!

Hans
  • 9
  • 5