पायथन में घातीय और लघुगणक वक्र फिटिंग कैसे करें? मैंने केवल बहुपद फिटिंग पाया


157

मेरे पास डेटा का एक सेट है और मैं तुलना करना चाहता हूं कि कौन सी रेखा इसका सबसे अच्छा वर्णन करती है (विभिन्न आदेशों के बहुपदों, घातीय या लघुगणक)।

मैं पायथन और नेम्पी का उपयोग करता हूं और बहुपद फिटिंग के लिए एक फ़ंक्शन है polyfit()। लेकिन मुझे एक्सपोनेंशियल और लॉगरिदमिक फिटिंग के लिए ऐसा कोई काम नहीं मिला।

क्या वहां पर कोई? या फिर इसे कैसे हल करें?

जवाबों:


222

फिटिंग के लिए y = A + B लॉग x , बस y (खिलाफ लॉग x ) फिट करें ।

>>> x = numpy.array([1, 7, 20, 50, 79])
>>> y = numpy.array([10, 19, 30, 35, 51])
>>> numpy.polyfit(numpy.log(x), y, 1)
array([ 8.46295607,  6.61867463])
# y ≈ 8.46 log(x) + 6.62

फिटिंग y = Ae Bx के लिए , दोनों ओर का लॉगरिदम ले लॉग y = log A + Bx देता है । तो एक्स के खिलाफ फिट (लॉग वाई ) ।

ध्यान दें कि फिटिंग (लॉग y ) मानो रैखिक है , y के छोटे मूल्यों पर जोर देगा , जिससे बड़े y के लिए बड़ा विचलन होगा । इसका कारण यह है है polyfit(रेखीय प्रतिगमन) Σ कम करके काम करता है मैंY ) 2 = Σ मैं ( वाई मैं - y मैं ) 2 । जब Y i = log y i , अवशेष = Y i = log (log y i ) ≈ / y i / | y i | तो भी अगरpolyfitबड़े वाई के लिए बहुत बुरा निर्णय लेता है , "डिवाइड-बाय- | वाई |" कारक इसके लिए क्षतिपूर्ति करेगा, जिससे polyfitछोटे मूल्यों का पक्ष होगा।

यह प्रत्येक प्रविष्टि एक "वजन" देने के लिए आनुपातिक द्वारा कम किया जा सकता है y । कीवर्ड तर्क के polyfitमाध्यम से भारित-कम-वर्ग का समर्थन करता wहै।

>>> x = numpy.array([10, 19, 30, 35, 51])
>>> y = numpy.array([1, 7, 20, 50, 79])
>>> numpy.polyfit(x, numpy.log(y), 1)
array([ 0.10502711, -0.40116352])
#    y ≈ exp(-0.401) * exp(0.105 * x) = 0.670 * exp(0.105 * x)
# (^ biased towards small values)
>>> numpy.polyfit(x, numpy.log(y), 1, w=numpy.sqrt(y))
array([ 0.06009446,  1.41648096])
#    y ≈ exp(1.42) * exp(0.0601 * x) = 4.12 * exp(0.0601 * x)
# (^ not so biased)

ध्यान दें कि एक्सेल, लिब्रे ऑफिस और अधिकांश वैज्ञानिक कैलकुलेटर आमतौर पर घातीय प्रतिगमन / प्रवृत्ति लाइनों के लिए अनवीटेड (बायस्ड) फॉर्मूला का उपयोग करते हैं। यदि आप चाहते हैं कि आपके परिणाम इन प्लेटफार्मों के अनुरूप हों, तो बेहतर परिणाम प्रदान करने पर भी भार शामिल न करें।


अब, यदि आप स्केपी का उपयोग कर सकते हैं, तो आप scipy.optimize.curve_fitबिना किसी बदलाव के किसी भी मॉडल को फिट करने के लिए उपयोग कर सकते हैं ।

के लिए y = एक + बी लॉग x परिणाम परिवर्तन विधि के समान ही है:

>>> x = numpy.array([1, 7, 20, 50, 79])
>>> y = numpy.array([10, 19, 30, 35, 51])
>>> scipy.optimize.curve_fit(lambda t,a,b: a+b*numpy.log(t),  x,  y)
(array([ 6.61867467,  8.46295606]), 
 array([[ 28.15948002,  -7.89609542],
        [ -7.89609542,   2.9857172 ]]))
# y ≈ 6.62 + 8.46 log(x)

के लिए y = Bx , तथापि, हम एक बेहतर फिट के बाद से यह Δ (लॉग गणना करता है प्राप्त कर सकते हैं y ) सीधे। लेकिन हमें एक प्रारंभिक अनुमान प्रदान करने की आवश्यकता है ताकि curve_fitवांछित स्थानीय न्यूनतम तक पहुंच सके।

>>> x = numpy.array([10, 19, 30, 35, 51])
>>> y = numpy.array([1, 7, 20, 50, 79])
>>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t),  x,  y)
(array([  5.60728326e-21,   9.99993501e-01]),
 array([[  4.14809412e-27,  -1.45078961e-08],
        [ -1.45078961e-08,   5.07411462e+10]]))
# oops, definitely wrong.
>>> scipy.optimize.curve_fit(lambda t,a,b: a*numpy.exp(b*t),  x,  y,  p0=(4, 0.1))
(array([ 4.88003249,  0.05531256]),
 array([[  1.01261314e+01,  -4.31940132e-02],
        [ -4.31940132e-02,   1.91188656e-04]]))
# y ≈ 4.88 exp(0.0553 x). much better.

घातीय प्रतिगमन की तुलना


2
@ टोमस: राइट। लॉग का आधार बदलना बस लॉग एक्स या लॉग वाई के लिए एक निरंतरता को गुणा करता है, जो r ^ 2 को प्रभावित नहीं करता है।
kennytm

4
यह छोटे y पर मूल्यों को अधिक वजन देगा। इसलिए ch_-squared मानों के लिए y_i द्वारा योगदान देना बेहतर है
Rupert Nash

17
कर्व फिटिंग के पारंपरिक अर्थों में यह समाधान गलत है। यह रैखिक अंतरिक्ष में अवशिष्ट के अभिव्यक्त वर्ग को कम नहीं करेगा, लेकिन लॉग स्पेस में। जैसा कि पहले उल्लेख किया गया है, यह प्रभावी रूप से बिंदुओं के भार को बदल देता है - वे अवलोकन जहां yछोटे होते हैं, कृत्रिम रूप से अधिक वजन वाले होंगे। फ़ंक्शन को परिभाषित करना बेहतर है (रैखिक, लॉग परिवर्तन नहीं) और वक्र फिटर या मिनिमाइज़र का उपयोग करें।
संतन

3
@ साइंटन ने घातीय प्रतिगमन में पूर्वाग्रह को संबोधित किया।
kennytm

2
वजन जोड़ने के लिए धन्यवाद! कई / अधिकांश लोगों को यह नहीं पता होता है कि यदि आप सिर्फ लॉग (डेटा) लेने की कोशिश करते हैं और इसके माध्यम से एक लाइन चलाते हैं (जैसे एक्सेल) तो आप कॉमली खराब परिणाम प्राप्त कर सकते हैं। जैसे मैं वर्षों से कर रहा था। जब मेरे बायेसियन शिक्षक ने मुझे यह दिखाया, तो मैं ऐसा था "लेकिन क्या वे शारीरिक रूप से [गलत] तरीके से नहीं सिखाते?" - "हाँ हम उस 'बेबी फिजिक्स' को कहते हैं, यह एक सरलीकरण है। इसे करने का सही तरीका है"।
DeusXMachina 18

102

तुम भी एक डेटा का एक सेट फिट कर सकते हैं जो कुछ भी करने के लिए आप का उपयोग कर की तरह कार्य curve_fitसे scipy.optimize। उदाहरण के लिए यदि आप एक घातीय कार्य ( प्रलेखन से ) फिट करना चाहते हैं :

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

x = np.linspace(0,4,50)
y = func(x, 2.5, 1.3, 0.5)
yn = y + 0.2*np.random.normal(size=len(x))

popt, pcov = curve_fit(func, x, yn)

और फिर यदि आप प्लॉट करना चाहते हैं, तो आप कर सकते हैं:

plt.figure()
plt.plot(x, yn, 'ko', label="Original Noised Data")
plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve")
plt.legend()
plt.show()

(नोट: *के सामने poptजब तुम में शर्तों का विस्तार होगा साजिश a, bऔर cकि func। उम्मीद कर रही है)


2
अच्छा लगा। क्या यह जांचने का कोई तरीका है कि हमें कितना अच्छा फील हुआ? R- चुकता मूल्य? क्या अलग-अलग अनुकूलन एल्गोरिथ्म पैरामीटर हैं जिन्हें आप बेहतर (या तेज) समाधान प्राप्त करने का प्रयास कर सकते हैं?
user391339

फिट की अच्छाई के लिए, आप फिट किए गए ऑप्टिमाइज़्ड मापदंडों को स्काइप ऑप्टिमाइज़ फंक्शन चिसक्वारे में फेंक सकते हैं; यह 2 मान लौटाता है, जिनमें से 2 पी-मान है।

कैसे मापदंडों का चयन करने पर कोई विचार a, bऔर c?
I_told_you_so

47

मुझे इससे कुछ परेशानी हो रही थी इसलिए मुझे बहुत स्पष्ट होने देना चाहिए ताकि मेरे जैसे कोई भी व्यक्ति समझ न सके।

कहते हैं कि हमारे पास एक डेटा फ़ाइल या ऐसा कुछ है

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
import sympy as sym

"""
Generate some data, let's imagine that you already have this. 
"""
x = np.linspace(0, 3, 50)
y = np.exp(x)

"""
Plot your data
"""
plt.plot(x, y, 'ro',label="Original Data")

"""
brutal force to avoid errors
"""    
x = np.array(x, dtype=float) #transform your data in a numpy array of floats 
y = np.array(y, dtype=float) #so the curve_fit can work

"""
create a function to fit with your data. a, b, c and d are the coefficients
that curve_fit will calculate for you. 
In this part you need to guess and/or use mathematical knowledge to find
a function that resembles your data
"""
def func(x, a, b, c, d):
    return a*x**3 + b*x**2 +c*x + d

"""
make the curve_fit
"""
popt, pcov = curve_fit(func, x, y)

"""
The result is:
popt[0] = a , popt[1] = b, popt[2] = c and popt[3] = d of the function,
so f(x) = popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3].
"""
print "a = %s , b = %s, c = %s, d = %s" % (popt[0], popt[1], popt[2], popt[3])

"""
Use sympy to generate the latex sintax of the function
"""
xs = sym.Symbol('\lambda')    
tex = sym.latex(func(xs,*popt)).replace('$', '')
plt.title(r'$f(\lambda)= %s$' %(tex),fontsize=16)

"""
Print the coefficients and plot the funcion.
"""

plt.plot(x, func(x, *popt), label="Fitted Curve") #same as line above \/
#plt.plot(x, popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3], label="Fitted Curve") 

plt.legend(loc='upper left')
plt.show()

परिणाम है: a = 0.849195983017, b = -1.18101681765, c = 2.24061176543, d = 0.816643894816

कच्चे डेटा और फिट फ़ंक्शन


8
y = [np.exp(i) for i in x]बहुत धीमा है; एक कारण सुन्न बनाया गया था ताकि आप लिख सकें y=np.exp(x)। इसके अलावा, उस प्रतिस्थापन के साथ, आप अपने क्रूर बल अनुभाग से छुटकारा पा सकते हैं। %timeitIn [27]: %timeit ylist=[exp(i) for i in x] 10000 loops, best of 3: 172 us per loop In [28]: %timeit yarr=exp(x) 100000 loops, best of 3: 2.85 us per loop
आईपिथॉन में

1
आपको धन्यवाद, आप सही हैं, लेकिन क्रूर बल भाग मुझे अभी भी उपयोग करने की आवश्यकता है जब मैं एक csv, xls या अन्य स्वरूपों से डेटा के साथ काम कर रहा हूं जो मैंने इस एल्गोरिथ्म का उपयोग करके सामना किया है। मुझे लगता है कि इसका उपयोग केवल तभी समझ में आता है जब कोई व्यक्ति किसी प्रयोगात्मक या सिमुलेशन डेटा से किसी फ़ंक्शन को फिट करने की कोशिश कर रहा है, और मेरे अनुभव में यह डेटा हमेशा अजीब स्वरूपों में आता है।
लिएंड्रो

3
x = np.array(x, dtype=float)आप धीमी सूची समझ से छुटकारा पाने के लिए सक्षम होना चाहिए।
अज्जाजा

8

वैसे मुझे लगता है कि आप हमेशा उपयोग कर सकते हैं:

np.log   -->  natural log
np.log10 -->  base 10
np.log2  -->  base 2

IanVS के उत्तर को थोड़ा संशोधित करना :

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def func(x, a, b, c):
  #return a * np.exp(-b * x) + c
  return a * np.log(b * x) + c

x = np.linspace(1,5,50)   # changed boundary conditions to avoid division by 0
y = func(x, 2.5, 1.3, 0.5)
yn = y + 0.2*np.random.normal(size=len(x))

popt, pcov = curve_fit(func, x, yn)

plt.figure()
plt.plot(x, yn, 'ko', label="Original Noised Data")
plt.plot(x, func(x, *popt), 'r-', label="Fitted Curve")
plt.legend()
plt.show()

यह निम्नलिखित ग्राफ में परिणाम देता है:

यहां छवि विवरण दर्ज करें


क्या एक संतृप्ति मूल्य फिट बैठता है? यदि हां, तो इसे कैसे एक्सेस किया जा सकता है?
बेन

7

यहाँ एक है linearization सरल डेटा कि से उपयोग करता है उपकरणों पर विकल्प scikit सीखना

दिया हुआ

import numpy as np

import matplotlib.pyplot as plt

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import FunctionTransformer


np.random.seed(123)

# General Functions
def func_exp(x, a, b, c):
    """Return values from a general exponential function."""
    return a * np.exp(b * x) + c


def func_log(x, a, b, c):
    """Return values from a general log function."""
    return a * np.log(b * x) + c


# Helper
def generate_data(func, *args, jitter=0):
    """Return a tuple of arrays with random data along a general function."""
    xs = np.linspace(1, 5, 50)
    ys = func(xs, *args)
    noise = jitter * np.random.normal(size=len(xs)) + jitter
    xs = xs.reshape(-1, 1)                                  # xs[:, np.newaxis]
    ys = (ys + noise).reshape(-1, 1)
    return xs, ys
transformer = FunctionTransformer(np.log, validate=True)

कोड

फिट घातीय डेटा

# Data
x_samp, y_samp = generate_data(func_exp, 2.5, 1.2, 0.7, jitter=3)
y_trans = transformer.fit_transform(y_samp)             # 1

# Regression
regressor = LinearRegression()
results = regressor.fit(x_samp, y_trans)                # 2
model = results.predict
y_fit = model(x_samp)

# Visualization
plt.scatter(x_samp, y_samp)
plt.plot(x_samp, np.exp(y_fit), "k--", label="Fit")     # 3
plt.title("Exponential Fit")

यहां छवि विवरण दर्ज करें

लॉग लॉग डेटा

# Data
x_samp, y_samp = generate_data(func_log, 2.5, 1.2, 0.7, jitter=0.15)
x_trans = transformer.fit_transform(x_samp)             # 1

# Regression
regressor = LinearRegression()
results = regressor.fit(x_trans, y_samp)                # 2
model = results.predict
y_fit = model(x_trans)

# Visualization
plt.scatter(x_samp, y_samp)
plt.plot(x_samp, y_fit, "k--", label="Fit")             # 3
plt.title("Logarithmic Fit")

यहां छवि विवरण दर्ज करें


विवरण

सामान्य चरण

  1. डेटा मान ( x, yया दोनों) में लॉग ऑपरेशन लागू करें
  2. डेटा को एक लीनियराइज़्ड मॉडल में फिर से रखें
  3. किसी भी लॉग ऑपरेशन (साथ np.exp()) को "उल्टा" करके और मूल डेटा पर फिट करके प्लॉट करें

हमारे डेटा को एक घातीय प्रवृत्ति मानकर, एक सामान्य समीकरण + हो सकता है:

यहां छवि विवरण दर्ज करें

हम बाद के समीकरण linearize कर सकते हैं (उदाहरण के लिए y = अवरोधन + ढलान * x) लेने के द्वारा लॉग :

यहां छवि विवरण दर्ज करें

एक रैखिक समीकरण ++ और प्रतिगमन मापदंडों को देखते हुए , हम गणना कर सकते हैं:

  • Aअवरोधन ( ln(A)) के माध्यम से
  • Bढलान के माध्यम से ( B)

रैखिककरण तकनीक का सारांश

Relationship |  Example   |     General Eqn.     |  Altered Var.  |        Linearized Eqn.  
-------------|------------|----------------------|----------------|------------------------------------------
Linear       | x          | y =     B * x    + C | -              |        y =   C    + B * x
Logarithmic  | log(x)     | y = A * log(B*x) + C | log(x)         |        y =   C    + A * (log(B) + log(x))
Exponential  | 2**x, e**x | y = A * exp(B*x) + C | log(y)         | log(y-C) = log(A) + B * x
Power        | x**2       | y =     B * x**N + C | log(x), log(y) | log(y-C) = log(B) + N * log(x)

+ ध्यान दें: शोर छोटा और C = 0 होने पर घातीय कार्यों को रेखीय करना सबसे अच्छा काम करता है। सावधानी से प्रयोग करें।

++ नोट: एक्स डेटा में बदलाव करते हुए घातांक डेटा को रैखिक बनाने में मदद करता है , वाई डेटा में परिवर्तन लॉग डेटा को रैखिक बनाने में मदद करता है ।


0

हम lmfitदोनों समस्याओं को हल करते हुए सुविधाओं को प्रदर्शित करते हैं।

दिया हुआ

import lmfit

import numpy as np

import matplotlib.pyplot as plt


%matplotlib inline
np.random.seed(123)

# General Functions
def func_log(x, a, b, c):
    """Return values from a general log function."""
    return a * np.log(b * x) + c


# Data
x_samp = np.linspace(1, 5, 50)
_noise = np.random.normal(size=len(x_samp), scale=0.06)
y_samp = 2.5 * np.exp(1.2 * x_samp) + 0.7 + _noise
y_samp2 = 2.5 * np.log(1.2 * x_samp) + 0.7 + _noise

कोड

दृष्टिकोण 1 - lmfitमॉडल

फिट घातीय डेटा

regressor = lmfit.models.ExponentialModel()                # 1    
initial_guess = dict(amplitude=1, decay=-1)                # 2
results = regressor.fit(y_samp, x=x_samp, **initial_guess)
y_fit = results.best_fit    

plt.plot(x_samp, y_samp, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()

यहां छवि विवरण दर्ज करें

दृष्टिकोण 2 - कस्टम मॉडल

लॉग लॉग डेटा

regressor = lmfit.Model(func_log)                          # 1
initial_guess = dict(a=1, b=.1, c=.1)                      # 2
results = regressor.fit(y_samp2, x=x_samp, **initial_guess)
y_fit = results.best_fit

plt.plot(x_samp, y_samp2, "o", label="Data")
plt.plot(x_samp, y_fit, "k--", label="Fit")
plt.legend()

यहां छवि विवरण दर्ज करें


विवरण

  1. एक प्रतिगमन वर्ग चुनें
  2. आपूर्ति नाम, प्रारंभिक अनुमान है कि फ़ंक्शन के डोमेन का सम्मान करते हैं

आप प्रतिगामी ऑब्जेक्ट से अनुमानित मापदंडों को निर्धारित कर सकते हैं। उदाहरण:

regressor.param_names
# ['decay', 'amplitude']

नोट: ExponentialModel()एक क्षय फ़ंक्शन का अनुसरण करता है , जो दो मापदंडों को स्वीकार करता है, जिनमें से एक नकारात्मक है।

यहां छवि विवरण दर्ज करें

यह भी देखें ExponentialGaussianModel(), जो अधिक मापदंडों को स्वीकार करता है ।

के माध्यम से पुस्तकालय स्थापित करें> pip install lmfit


0

वुल्फ्राम में एक घातीय फिटिंग के लिए एक बंद रूप समाधान है । उनके पास लॉगरिदमिक और पावर लॉ फिट करने के लिए समान समाधान हैं ।

मुझे यह scipy के कर्व_फिट से बेहतर काम करने के लिए मिला। यहाँ एक उदाहरण है:

import numpy as np
import matplotlib.pyplot as plt

# Fit the function y = A * exp(B * x) to the data
# returns (A, B)
# From: https://mathworld.wolfram.com/LeastSquaresFittingExponential.html
def fit_exp(xs, ys):
    S_x2_y = 0.0
    S_y_lny = 0.0
    S_x_y = 0.0
    S_x_y_lny = 0.0
    S_y = 0.0
    for (x,y) in zip(xs, ys):
        S_x2_y += x * x * y
        S_y_lny += y * np.log(y)
        S_x_y += x * y
        S_x_y_lny += x * y * np.log(y)
        S_y += y
    #end
    a = (S_x2_y * S_y_lny - S_x_y * S_x_y_lny) / (S_y * S_x2_y - S_x_y * S_x_y)
    b = (S_y * S_x_y_lny - S_x_y * S_y_lny) / (S_y * S_x2_y - S_x_y * S_x_y)
    return (np.exp(a), b)


xs = [33, 34, 35, 36, 37, 38, 39, 40, 41, 42]
ys = [3187, 3545, 4045, 4447, 4872, 5660, 5983, 6254, 6681, 7206]

(A, B) = fit_exp(xs, ys)

plt.figure()
plt.plot(xs, ys, 'o-', label='Raw Data')
plt.plot(xs, [A * np.exp(B *x) for x in xs], 'o-', label='Fit')

plt.title('Exponential Fit Test')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='best')
plt.tight_layout()
plt.show()

यहां छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.