Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tut] Exponential Fit with SciPy’s curve_fit()

#1
Exponential Fit with SciPy’s curve_fit()

<div><p>In this article, you’ll explore how to generate exponential fits by exploiting the <code>curve_fit()</code> function from the Scipy library. SciPy’s <code>curve_fit()</code> allows building custom fit functions with which we can describe data points that follow an exponential trend. </p>
<ul>
<li>In the first part of the article, the <em><code>curve_fit()</code></em> function is used to fit the exponential trend of the number of COVID-19 cases registered in California (CA). </li>
<li>The second part of the article deals with fitting histograms, characterized, also in this case, by an exponential trend.</li>
</ul>
<p><strong>Disclaimer</strong>: <em>I’m not a virologist, I suppose that the fitting of a viral infection is defined by more complicated and accurate models; however, the only aim of this article is to show how to apply an exponential fit to model (to a certain degree of approximation) the increase in the total infection cases from the COVID-19. </em></p>
<h2>Exponential fit of COVID-19 total cases in California</h2>
<p>Data related to the COVID-19 pandemic have been obtained from the official website of the “Centers for Disease Control and Prevention” (<a href="https://data.cdc.gov/Case-Surveillance/United-States-COVID-19-Cases-and-Deaths-by-State-o/9mfq-cb36">https://data.cdc.gov/Case-Surveillance/United-States-COVID-19-Cases-and-Deaths-by-State-o/9mfq-cb36</a>) and downloaded as a .csv file. The first thing to do is to import the data into a Pandas dataframe. To do this, the Pandas functions <em>pandas.read_csv() </em>and <em>pandas.Dataframe()</em> were employed. The created dataframe is made up of 15 columns, among which we can find the submission_date, the state, the total cases, the confirmed cases and other related observables. To gain an insight into the order in which these categories are displayed, we print the header of the dataframe; as can be noticed, the total cases are listed under the voice “tot_cases”.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="158" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-66.png" alt="" class="wp-image-17512" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-66.png 643w, https://blog.finxter.com/wp-content/uplo...300x74.png 300w, https://blog.finxter.com/wp-content/uplo...40x158.png 640w, https://blog.finxter.com/wp-content/uplo...150x37.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>Since in this article we are only interested in the data related to the California, we create a sub-dataframe that contains only the information related to the California state. To do that, we exploit the potential of Pandas in indexing subsections of a dataframe. This dataframe will be called df_CA (from California) and contains all the elements of the main dataframe for which the column “state” is equal to “CA”. After this step, we can build two arrays, one (called <em>tot_cases</em>) that contains the total cases (the name of the respective header column is “tot_cases”) and one that contains the number of days passed by the first recording (called <em>days</em>). Since the data were recorded daily, in order to build the “days” array, we simply build an array of equally spaced integer number from 0 to the length of the “tot_cases” array, in this way, each number refers to the n° of days passed from the first recording (day 0).</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="37" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-67.png" alt="" class="wp-image-17513" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-67.png 643w, https://blog.finxter.com/wp-content/uplo...300x17.png 300w, https://blog.finxter.com/wp-content/uplo...640x37.png 640w, https://blog.finxter.com/wp-content/uplo...-150x9.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>At this point, we can define the function that will be used by <code>curve_fit()</code><em> </em>to fit the created dataset. An exponential function is defined by the equation:</p>
<p><em><strong>y = a*exp(b*x) +c</strong></em></p>
<p>where <em>a, b</em> and <em>c</em> are the fitting parameters. We will hence define the function <code>exp_fit()</code> which return the exponential function, <em>y</em>, previously defined. The <code>curve_fit()</code> function takes as necessary input the fitting function that we want to fit the data with, the x and y arrays in which are stored the values of the datapoints. It is also possible to provide initial guesses for each of the fitting parameters by inserting them in a list called <code>p0 = […]</code> and upper and lower boundaries for these parameters (for a comprehensive description of the <code>curve_fit()</code> function, please refer to <a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html">https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html</a> ). In this example, we will only provide initial guesses for our fitting parameters. Moreover, we will only fit the total cases of the first 200 days; this is because for the successive days, the number of cases didn’t follow an exponential trend anymore (possibly due to a decrease in the number of new cases). To refer only to the first 200 values of the arrays “days” and “tot_cases”, we exploit array slicing (e.g. days[:200]).</p>
<p>The output of <code>curve_fit()</code> are the fitting parameters, presented in the same order that was used during their definition, within the fitting function. Keeping this in mind, we can build the array that contains the fitted results, calling it <code>“fit_eq”</code>.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="93" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-68.png" alt="" class="wp-image-17514" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-68.png 643w, https://blog.finxter.com/wp-content/uplo...300x43.png 300w, https://blog.finxter.com/wp-content/uplo...640x93.png 640w, https://blog.finxter.com/wp-content/uplo...150x22.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>Now that we built the fitting array, we can plot both the original data points and their exponential fit.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="111" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-69.png" alt="" class="wp-image-17515" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-69.png 643w, https://blog.finxter.com/wp-content/uplo...300x52.png 300w, https://blog.finxter.com/wp-content/uplo...40x111.png 640w, https://blog.finxter.com/wp-content/uplo...150x26.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>The final result will be a plot like the one in Figure 1:</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="626" height="324" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-70.png" alt="" class="wp-image-17516" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-70.png 626w, https://blog.finxter.com/wp-content/uplo...00x155.png 300w, https://blog.finxter.com/wp-content/uplo...150x78.png 150w" sizes="(max-width: 626px) 100vw, 626px" /><figcaption><em><strong>Figure 1</strong></em></figcaption></figure>
</p>
<h2>Application of an exponential fit to histograms</h2>
<p>Now that we know how to define and use an exponential fit, we will see how to apply it to the data displayed on a histogram. Histograms are frequently used to display the distributions of specific quantities like prices, heights etc…The most common type of distribution is the Gaussian distribution; however, some types of observables can be defined by a decaying exponential distribution. In a decaying exponential distribution, the frequency of the observables decreases following an <a>exponential</a><a href="#_msocom_1">[A1]</a>  trend; a possible example is the amount of time that the battery of your car will last (i.e. the probability of having a battery lasting for long periods decreases exponentially). The exponentially decaying array will be defined by exploiting the Numpy function <em>random.exponential(). </em>According to the Numpy documentation, the <em>random.exponential() </em>function draws samples from an exponential distribution; it takes two inputs, the “scale” which is a parameter defining the exponential decay and the “size” which is the length of the array that will be generated. Once obtained random values from an exponential distribution, we have to generate the histogram; to do this, we employ another Numpy function, called <em>histogram(), </em>which generates an histogram taking as input the distribution of the data (we set the binning to “auto”, in this way the width of the bins is automatically computed). The output of <em>histogram()</em> is a 2D array; the first array contains the  frequencies of the distribution while the second one contains the edges of the bins. Since we are only interested in the frequencies, we assign the first output to the variable “hist”. For this example, we will generate the array containing the bin position by using the Numpy <em>arange()</em> function; the bins will have a width of 1 and their number will be equal to the number of elements contained in the “hist” array.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="49" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-71.png" alt="" class="wp-image-17517" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-71.png 643w, https://blog.finxter.com/wp-content/uplo...300x23.png 300w, https://blog.finxter.com/wp-content/uplo...640x49.png 640w, https://blog.finxter.com/wp-content/uplo...150x11.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>At this point, we have to define the fitting function and to call <em>curve_fit() </em>for the values of the just created histogram. The equation describing an exponential decay is similar to the one defined in the first part; the only difference is that the exponent has a negative sign, this allows the values to decrease according to an exponential fashion. Since the elements in the “x” array, defined for the bin position, are the coordinates of the left edge of each bin, we define another x array that stores the position of the center of each bin (called “x_fit”); this allows the fitting curve to pass through the center of each bin, leading to a better visual impression. This array will be defined by taking the values of the left side of the bins (“x” array elements) and adding half the bin size; which corresponds to half the value of the second bin position (element of index 1). Similar to the previous part, we now call <em>curve_fit(), </em>generate the fitting array and assign it to the varaible “fit_eq”.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="105" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-72.png" alt="" class="wp-image-17518" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-72.png 643w, https://blog.finxter.com/wp-content/uplo...300x49.png 300w, https://blog.finxter.com/wp-content/uplo...40x105.png 640w, https://blog.finxter.com/wp-content/uplo...150x24.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>Once the distribution has been fitted, the last thing to do is to check the result by plotting both the histogram and the fitting function. In order to plot the histogram, we will use the matplotlib function <em>bar()</em>, while the fitting function will be plotted using the classical <em>plot() </em>function.</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="643" height="61" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-73.png" alt="" class="wp-image-17519" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-73.png 643w, https://blog.finxter.com/wp-content/uplo...300x28.png 300w, https://blog.finxter.com/wp-content/uplo...640x61.png 640w, https://blog.finxter.com/wp-content/uplo...150x14.png 150w" sizes="(max-width: 643px) 100vw, 643px" /></figure>
<p>The final result is displayed in Figure 2:</p>
<figure class="wp-block-image size-large"><img loading="lazy" width="620" height="321" src="https://blog.finxter.com/wp-content/uploads/2020/11/image-74.png" alt="" class="wp-image-17520" srcset="https://blog.finxter.com/wp-content/uploads/2020/11/image-74.png 620w, https://blog.finxter.com/wp-content/uplo...00x155.png 300w, https://blog.finxter.com/wp-content/uplo...150x78.png 150w" sizes="(max-width: 620px) 100vw, 620px" /><figcaption>Figure 2</figcaption></figure>
</p>
<h2>Summary</h2>
<p>In these two examples, the <code>curve_fit()</code><em> </em>function was used to apply to different exponential fits to specific data points. However, the power of the <code>curve_fit()</code><em> </em>function, is that it allows you defining your own custom fit functions, being them linear, polynomial or logarithmic functions. The procedure is identical to the one shown in this article, the only difference is in the shape of the function that you have to define before calling <code>curve_fit()</code>.</p>
<hr class="wp-block-separator"/>
<h2>Full Code</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit url = "United_States_COVID-19_Cases_and_Deaths_by_State_over_Time" #url of the .csv file
file = pd.read_csv(url, sep = ';', thousands = ',') # import the .csv file
df = pd.DataFrame(file) # build up the pandas dataframe
print(df.columns) #visualize the header
df_CA = df[df['state'] == 'CA'] #initialize a sub-dataframe for storing only the values for the California
tot_cases = np.array((df_CA['tot_cases'])) #create an array with the total n° of cases
days = np.linspace(0, len(tot_cases), len(tot_cases)) # array containing the n° of days from the first recording #DEFINITION OF THE FITTING FUNCTION
def exp_fit(x, a, b, c): y = a*np.exp(b*x) + c return y #----CALL THE FITTING FUNCTION----
fit = curve_fit(exp_fit,days[:200],tot_cases[:200], p0 = [0.005, 0.03, 5])
fit_eq = fit[0][0]*np.exp(fit[0][1]*days[:200])+fit[0][2] # #----PLOTTING-------
fig = plt.figure()
ax = fig.subplots()
ax.scatter(days[:200], tot_cases[:200], color = 'b', s = 5)
ax.plot(days[:200], fit_eq, color = 'r', alpha = 0.7)
ax.set_ylabel('Total cases')
ax.set_xlabel('N° of days')
plt.show() #-----APPLY AN EXPONENTIAL FIT TO A HISTOGRAM--------
data = np.random.exponential(5, size=10000) #generating a random exponential distribution
hist = np.histogram(data, bins="auto")[0] #generating a histogram from the exponential distribution
x = np.arange(0, len(hist), 1) # generating an array that contains the coordinated of the left edge of each bar #---DECAYING FIT OF THE DISTRIBUTION----
def exp_fit(x,a,b): #defining a decaying exponential function y = a*np.exp(-b*x) return y x_fit = x + x[1]/2 # the point of the fit will be positioned at the center of the bins
fit_ = curve_fit(exp_fit,x_fit,hist) # calling the fit function
fit_eq = fit_[0][0]*np.exp(-fit_[0][1]*x_fit) # building the y-array of the fit
#Plotting
plt.bar(x,hist, alpha = 0.5, align = 'edge', width = 1)
plt.plot(x_fit,fit_eq, color = 'red')
plt.show()
</pre>
<p>The post <a href="https://blog.finxter.com/exponential-fit-with-scipys-curve_fit/" target="_blank" rel="noopener noreferrer">Exponential Fit with SciPy’s curve_fit()</a> first appeared on <a href="https://blog.finxter.com/" target="_blank" rel="noopener noreferrer">Finxter</a>.</p>
</div>


https://www.sickgaming.net/blog/2020/11/...curve_fit/
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

Forum software by © MyBB Theme © iAndrew 2016