Lesson 3. Write Functions with Multiple Parameters in Python
Learning Objectives
- Write and execute custom functions with multiple input parameters in Python.
- Write and execute custom functions with optional input parameters in Python.
How to Define a Function with Multiple Parameters in Python
Previously in this textbook, you learned that an input parameter is the required information that you pass to the function for it to run successfully. The function will take the value or object provided as the input parameter and use it to perform some task.
You also learned that in Python, the required parameter can be defined using a placeholder variable, such as data
, which represents the value or object that will be acted upon in the function.
def function_name(data):
However, sometimes you may need additional information for the function to run successfully.
Luckily, you can write functions that take in more than one parameter by defining as many parameters as needed, for example:
def function_name(data_1, data_2):
When the function is called, a user can provide any value for data_1
or data_2
that the function can take as an input for that parameter (e.g. single value variable, list, numpy array, pandas dataframe column).
Write a Function with Multiple Parameters in Python
Imagine that you want to define a function that will take in two numeric values as inputs and return the product of these input values (i.e. multiply the values).
Begin with the def
keyword and the function name, just as you have before to define a function:
def multiply_values
Next, provide two placeholder variable names for the input parameters, as shown below.
def multiply_values(x,y):
Add the code to multiply the values and the return
statement to returns the product of the two values:
def multiply_values(x,y):
z = x * y
return z
Last, write a docstring to provide the details about this function, including a brief description of the function (i.e. how it works, purpose) as well as identify the input parameters (i.e. type, description) and the returned output (i.e. type, description).
def multiply_values(x,y):
"""Calculate product of two inputs.
Parameters
----------
x : int or float
y : int or float
Returns
------
z : int or float
"""
z = x * y
return z
Call Custom Functions with Multiple Parameters in Python
Now that you have defined the function multiple_values()
, you can call it by providing values for the two input parameters.
# Call function with numeric values
multiply_values(x = 0.7, y = 25.4)
17.779999999999998
Recall that you can also provide pre-defined variables as inputs, for example, a value for precipitation and another value for a unit conversion value.
# Average monthly precip (inches) for Jan in Boulder, CO
precip_jan_in = 0.7
# Conversion factor from inches to millimeters
to_mm = 25.4
# Call function with pre-defined variables
precip_jan_mm = multiply_values(
x = precip_jan_in,
y = to_mm)
precip_jan_mm
17.779999999999998
Note that the function is not defined specifically for unit conversions, but as it completes a generalizable task, it can be used for simple unit conversions.
Combine Unit Conversion and Calculation of Statistics into One Function
Now imagine that you want to both convert the units of a numpy array from millimeters to inches and calculate the mean value along a specified axis for either columns or rows.
Recall the function definition that you previously wrote to convert values from millimeters to inches:
def mm_to_in(mm):
"""Convert input from millimeters to inches.
Parameters
----------
mm : int or float
Numeric value with units in millimeters.
Returns
------
inches : int or float
Numeric value with units in inches.
"""
inches = mm / 25.4
return inches
You can expand this function to include running a mean along a specified axis for columns or rows, and then use this function over and over on many numpy arrays as needed.
This new function can have descriptive names for the function and the input parameters that describe more clearly what the function accomplishes.
Begin by defining the function with a descriptive name and the two necessary parameters:
- the input array with values in millimeters
- the axis value for the mean calculation
Use placeholder variable names that highlight the purpose of each parameter:
def mean_mm_to_in_arr(arr_mm, axis_value):
Next, add the code to first calculate the mean of the input array along a specified axis, and then to convert the mean values from millimeters to inches.
First, add the code line to calculate a mean along a specified axis.
def mean_mm_to_in_arr(arr_mm, axis_value):
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
Next, add the code line to convert the mean array from millimeters to inches. In this case, the return
statement should return the mean array in inches.
def mean_mm_to_in_arr(arr_mm, axis_value):
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
mean_arr_in = mean_arr_mm / 25.4
return mean_arr_in
Note that the function could be written to convert the values first and then calculate the mean. However, given that the function will complete both tasks and return the mean values in the desired units, it is more efficient to calculate the mean values first and then convert just those values, rather than converting all of the values in the input array.
Last, include a docstring to provide the details about this function, including a brief description of the function (i.e. how it works, purpose) as well as identify the input parameters (i.e. type, description) and the returned output (i.e. type, description).
def mean_mm_to_in_arr(arr_mm, axis_value):
"""Calculate mean values of input array along a specified
axis and convert values from millimeters to inches.
Parameters
----------
arr_mm : numpy array
Numeric values in millimeters.
axis_value : int
0 to calculate mean for each column.
1 to calculate mean for each row.
Returns
------
mean_arr_in : numpy array
Mean values of input array in inches.
"""
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
mean_arr_in = mean_arr_mm / 25.4
return mean_arr_in
Now that you have defined mean_mm_to_in_arr()
, you can call the function with the appropriate input parameters.
Create some data and test your new function with different input values for the axis_value
parameter.
# Import necessary package to run function
import numpy as np
# 2d array of average monthly precip (mm) for 2002 and 2013 in Boulder, CO
precip_2002_2013_mm = np.array([[27.178, 11.176, 38.1, 5.08, 81.28, 29.972,
2.286, 36.576, 38.608, 61.976, 19.812, 0.508],
[6.858, 28.702, 43.688, 105.156, 67.564, 15.494,
26.162, 35.56 , 461.264, 56.896, 7.366, 12.7]
])
# Calculate monthly mean (inches) for precip_2002_2013
monthly_mean_in = mean_mm_to_in_arr(arr_mm = precip_2002_2013_mm,
axis_value = 0)
monthly_mean_in
array([0.67 , 0.785, 1.61 , 2.17 , 2.93 , 0.895, 0.56 , 1.42 , 9.84 ,
2.34 , 0.535, 0.26 ])
# Calculate yearly mean (inches) for precip_2002_2013
yearly_mean_in = mean_mm_to_in_arr(arr_mm = precip_2002_2013_mm,
axis_value = 1)
yearly_mean_in
array([1.15666667, 2.84583333])
Define Optional Input Parameters for a Function
Your previously defined function works well if you want to use a specified axis for the mean.
However, notice what happens when you try to call the function without providing an axis value, such as for a one-dimensional array.
# 1d array of average monthly precip (mm) for 2002 in Boulder, CO
precip_2002_mm = np.array([27.178, 11.176, 38.1, 5.08, 81.28, 29.972,
2.286, 36.576, 38.608, 61.976, 19.812, 0.508])
# Calculate mean (inches) for precip_2002
monthly_mean_in = mean_mm_to_in_arr(arr_mm = precip_2002_mm)
You get an error that the axis_value
is missing:
TypeError: mean_mm_to_in_arr() missing 1 required positional argument: 'axis_value'
What if you want to make the function more generalizable, so that the axis value is optional?
You can do that by specifying a default value for axis_value
as None
as shown below:
def mean_mm_to_in_arr(arr_mm, axis_value=None):
The function will assume that the axis value is None
(i.e. that an input value has not been provided by the user), unless specified otherwise in the function call.
However, as written, the original function code uses the axis value to calculate the mean, so you need to make a few more changes, so that the mean code runs with an axis value if a value is provided or runs without an axis value if one is not provided.
Luckily, you have already learned about conditional statements, which you can now add to your function to run the mean code with or without an axis value as needed.
Using a conditional statement, you can check if axis_value
is equal to None
, in which case the mean code will run without an axis value.
def mean_mm_to_in_arr(arr_mm, axis_value=None):
if axis_value is None:
mean_arr_mm = np.mean(arr_mm)
The else
statement would mean that axis_value
is not equal to None
(i.e. a user has provided an input value) and thus would run the mean code with the specified axis value.
def mean_mm_to_in_arr(arr_mm, axis_value=None):
if axis_value is None:
mean_arr_mm = np.mean(arr_mm)
else:
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
The code for the unit conversion and the return
remain the same, just with updated names:
def mean_mm_to_in_arr(arr_mm, axis_value=None):
if axis_value is None:
mean_arr_mm = np.mean(arr_mm)
else:
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
mean_arr_in = mean_arr_mm / 25.4
return mean_arr_in
Last, include a docstring to provide the details about this revised function. Notice that the axis value has been labeled optional in the docstring.
def mean_mm_to_in_arr(arr_mm, axis_value=None):
"""Calculate mean values of input array and convert values
from millimeters to inches. If an axis is specified,
the mean will be calculated along that axis.
Parameters
----------
arr_mm : numpy array
Numeric values in millimeters.
axis_value : int (optional)
0 to calculate mean for each column.
1 to calculate mean for each row.
Returns
------
mean_arr_in : numpy array
Mean values of input array in inches.
"""
if axis_value is None:
mean_arr_mm = np.mean(arr_mm)
else:
mean_arr_mm = np.mean(arr_mm, axis = axis_value)
mean_arr_in = mean_arr_mm / 25.4
return mean_arr_in
Notice that the function will return the same output as before for the two-dimensional array precip_2002_2013_mm
.
# Calculate monthly mean (inches) for precip_2002_2013
monthly_mean_in = mean_mm_to_in_arr(arr_mm = precip_2002_2013_mm,
axis_value = 0)
monthly_mean_in
array([0.67 , 0.785, 1.61 , 2.17 , 2.93 , 0.895, 0.56 , 1.42 , 9.84 ,
2.34 , 0.535, 0.26 ])
However, now you can also provide a one-dimensional array as an input without a specified axis and receive the appropriate output.
# Calculate mean (inches) for precip_2002
monthly_mean_in = mean_mm_to_in_arr(arr_mm = precip_2002_mm)
monthly_mean_in
1.1566666666666667
Combine Download and Import of Data Files into One Function
You can also write multi-parameter functions to combine other tasks into one function, such as downloading and importing data files into a pandas dataframe.
Think about the code that you need to include in the function:
- download data file from URL:
et.data.get_data(url=file_url)
- import data file into pandas dataframe:
pd.read_csv(path)
From this code, you can see that you will need two input parameters for the combined function:
- the URL to the data file
- the path to the downloaded file
Begin by specifying a function name and the placeholder variable names for the necessary input parameters.
def download_import_df(file_url, path):
Next, add the code for download and the import.
def download_import_df(file_url, path):
et.data.get_data(url=file_url)
df = pd.read_csv(path)
However, what if the working directory has not been set before this function is called, and you do not want to use absolute paths?
Since you know that the get_data()
function creates the earth-analytics
directory under the home directory if it does not already exist, you can safely assume that this combined function will also create that directory.
As such, you can include setting the working directory in the function, so that you do not have to worry about providing absolute paths to the function:
def download_import_df(file_url, path):
et.data.get_data(url=file_url)
os.chdir(os.path.join(et.io.HOME, "earth-analytics"))
df = pd.read_csv(path)
return df
Last, include a docstring to provide the details about this function, including a brief description of the function (i.e. how it works, purpose) as well as identify the input parameters (i.e. type, description) and the returned output (i.e. type, description).
def download_import_df(file_url, path):
"""Download file from specified URL and import file
into a pandas dataframe from a specified path.
Working directory is set to earth-analytics directory
under home, which is automatically created by the
download.
Parameters
----------
file_url : str
URL to CSV file (http or https).
path : str
Path to CSV file using relative path
to earth-analytics directory under home.
Returns
------
df : pandas dataframe
Dataframe imported from downloaded CSV file.
"""
et.data.get_data(url=file_url)
os.chdir(os.path.join(et.io.HOME, "earth-analytics"))
df = pd.read_csv(path)
return df
Now that you have defined the function, you can import the packages needed to run the function and define the variables that you will use as input parameters.
# Import necessary packages to run function
import os
import pandas as pd
import earthpy as et
# URL for average monthly precip (inches) for 2002 and 2013 in Boulder, CO
precip_2002_2013_df_url = "https://ndownloader.figshare.com/files/12710621"
# Path to downloaded .csv file with headers
precip_2002_2013_df_path = os.path.join("data", "earthpy-downloads",
"precip-2002-2013-months-seasons.csv")
Using these variables, you can now call the function to download and import the file into a pandas dataframe.
# Create dataframe using download/import function
precip_2002_2013_df = download_import_df(
file_url = precip_2002_2013_df_url,
path = precip_2002_2013_df_path)
precip_2002_2013_df
Downloading from https://ndownloader.figshare.com/files/12710621
months | precip_2002 | precip_2013 | seasons | |
---|---|---|---|---|
0 | Jan | 1.07 | 0.27 | Winter |
1 | Feb | 0.44 | 1.13 | Winter |
2 | Mar | 1.50 | 1.72 | Spring |
3 | Apr | 0.20 | 4.14 | Spring |
4 | May | 3.20 | 2.66 | Spring |
5 | June | 1.18 | 0.61 | Summer |
6 | July | 0.09 | 1.03 | Summer |
7 | Aug | 1.44 | 1.40 | Summer |
8 | Sept | 1.52 | 18.16 | Fall |
9 | Oct | 2.44 | 2.24 | Fall |
10 | Nov | 0.78 | 0.29 | Fall |
11 | Dec | 0.02 | 0.50 | Winter |
Making Functions More Efficient Does Not Always Mean More Parameters
Note that you previously defined download_import_df()
to take in two parameters, one for the URL and for the path, and the function works well to accomplish the task.
However, with a little investigation into the et.data.get_data()
function, you can see that the output of that function is actually a path to the downloaded file!
help(et.data.get_data)
In the docstring details provided, you can see that the full path to the downloaded data is returned by the function:
Returns
-------
path_data : str
The path to the downloaded data.
This means that you can redefine download_import_df()
to be more efficient by simply using the output of the et.data.get_data()
function as the input to the pd.read_csv()
function.
Now, you actually only need one parameter for the URL and you do not have to define the working directory in the function, in order to find the appropriate file.
def download_import_df(file_url):
"""Download file from specified URL and import file
into a pandas dataframe.
The path to the downloaded file is automatically
generated by the download and is passed to the
pandas function to create a new dataframe.
Parameters
----------
file_url : str
URL to CSV file (http or https).
Returns
------
df : pandas dataframe
Dataframe imported from downloaded CSV file.
"""
df = pd.read_csv(et.data.get_data(url=file_url))
return df
Your revised function now executes only one line, rather than three lines! Note that the docstring was also updated to reflect that there is only one input parameter for this function.
Now you can call the function with just a single parameter for the URL.
# Create dataframe using download/import function
precip_2002_2013_df = download_import_df(
file_url = precip_2002_2013_df_url)
precip_2002_2013_df
months | precip_2002 | precip_2013 | seasons | |
---|---|---|---|---|
0 | Jan | 1.07 | 0.27 | Winter |
1 | Feb | 0.44 | 1.13 | Winter |
2 | Mar | 1.50 | 1.72 | Spring |
3 | Apr | 0.20 | 4.14 | Spring |
4 | May | 3.20 | 2.66 | Spring |
5 | June | 1.18 | 0.61 | Summer |
6 | July | 0.09 | 1.03 | Summer |
7 | Aug | 1.44 | 1.40 | Summer |
8 | Sept | 1.52 | 18.16 | Fall |
9 | Oct | 2.44 | 2.24 | Fall |
10 | Nov | 0.78 | 0.29 | Fall |
11 | Dec | 0.02 | 0.50 | Winter |
Practice Writing Multi-Parameter Functions for Pandas Dataframes
You have a function that combines the mean calculation along a specified axis and the conversion from millimeters to inches for a numpy array.
How might you need to change this function to create a similar function for pandas dataframe, but now converting from inches to millimeters?
For the mean, you can run summary statistics on pandas using a specified axis (just like a numpy array) with the following code:
df.mean(axis = axis_value)
With the axis value 0
, the code will calculate a mean for each numeric column in the dataframe.
With the axis value 1
, the code will calculate a mean for each row with numeric values in the dataframe.
Think about which code lines in the existing function mean_mm_to_in_arr()
can be modified to run the equivalent code on a pandas dataframe.
Note that the df.mean(axis = axis_value)
returns the mean values of a dataframe (along the specified axis) as a pandas series.
Practice Writing Multi-Parameter Functions for Numpy Arrays
You also have a function that combines the data download and import for a pandas dataframe, you can modify the function for other data structures such as a numpy array.
How might you need to change this function to create an equivalent for numpy arrays?
Think about which code lines in the existing function download_import_df()
can be modified to write a new function that downloads and imports data into a numpy array.
To begin, you may want to write one function for a 1-dimensional array and another function for a 2-dimensional array.
To advance in your practice, you can think about adding a conditional statement that would check for the file type (.txt for a 1-dimensional array .csv for a 2-dimensional array) before executing the appropriate import code.
Leave a Comment