Python'da üstel ve logaritmik eğri uydurma nasıl yapılır? Sadece polinom uydurma buldum


157

Ben bir veri kümesi var ve hangi satır en iyi açıklar karşılaştırmak istiyorum (farklı siparişlerin polinomları, üstel veya logaritmik).

Python ve Numpy kullanıyorum ve polinom uydurma için bir fonksiyon var polyfit(). Ancak üstel ve logaritmik fitting için böyle bir işlev bulamadım.

Orada hiç? Yoksa nasıl çözülür?

Yanıtlar:


222

Uydurma için y = A + B günlük x , sadece uygun y (log karşı 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 montajı için, her iki tarafın logaritmasını alın log y = log A + Bx verir . Bu yüzden x'e (log y ) uyun .

Bağlantı elemanının (log y ) lineermiş gibi y'nin küçük y değerlerini vurgulayacağını ve büyük y için büyük sapmaya neden olacağını unutmayın . Çünkü polyfit(doğrusal regresyon)) iY ) 2 = ∑ i ( Y i - Ŷ i ) 2'yi minimize ederek çalışır . Zaman , Y i = log y ı , artıklar Δ Y i Δ = (log y i ) ≈ Δ y ı / | y i |. Yani bilepolyfitbüyük y için çok kötü bir karar verir , "bölü | | y |" faktörü telafi edecek ve polyfitküçük değerlere neden olacaktır .

Bu, her girişe y ile orantılı bir "ağırlık" verilerek hafifletilebilir . anahtar kelime bağımsız değişkeni polyfitüzerinden ağırlıklı en küçük kareleri destekler 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)

Excel, LibreOffice ve çoğu bilimsel hesap makinesinin genellikle üstel regresyon / trend çizgileri için ağırlıksız (önyargılı) formülü kullandığını unutmayın. Sonuçlarınızın bu platformlarla uyumlu olmasını istiyorsanız, daha iyi sonuçlar alsa bile ağırlıkları eklemeyin.


Şimdi, scipy kullanabiliyorsanız scipy.optimize.curve_fit, herhangi bir modele dönüştürmeden sığdırmak için kullanabilirsiniz .

İçin y = A + B günlük x sonucu dönüştürme yöntemi olarak aynıdır:

>>> 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)

İçin y = Ae Bx o ö (log hesaplar, ancak, çünkü biz daha iyi bir uyum elde edebilirsiniz y doğrudan). Ancak curve_fit, istenen yerel minimum değere ulaşabilmek için bir başlangıç ​​tahmini sağlamalıyız .

>>> 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.

üstel regresyonun karşılaştırılması


2
@Tomas: Doğru. Günlük tabanını değiştirmek, x veya log y ile sabitlemek için r ^ 2'yi etkilemeyen bir sabiti çarpar.
kennytm

4
Bu, küçük y'deki değerlere daha fazla ağırlık verecektir. Bu nedenle, y_i tarafından ki kare değerlere katkıların ağırlıklandırılması daha iyidir
Rupert Nash

17
Bu çözüm geleneksel eğri uydurma anlamında yanlıştır. Doğrusal uzayda artıkların toplam karesini en aza indirmez, ancak kütük alanında en aza indirir. Daha önce de belirtildiği gibi, bu noktaların ağırlığını etkili bir şekilde değiştirir - yküçük olan gözlemler yapay olarak fazla ağırlıklandırılır. İşlevi tanımlamak (günlük dönüşümü değil doğrusal) ve bir eğri tesisatçısı veya minimizer kullanmak daha iyidir.
santon

3
@santon Üstel regresyondaki yanlılığı giderdi.
kennytm

2
Ağırlığı eklediğiniz için teşekkür ederiz! Çoğu / çoğu kişi, yalnızca günlük (veri) alıp içinden bir çizgi (Excel gibi) çalıştırmaya çalıştığınızda komik olarak kötü sonuçlar alabileceğinizi bilmez. Yıllardır yaptığım gibi. Bayesli öğretmenim bunu gösterdiğinde, "Ama fizikteki [yanlış] yolu öğretmiyorlar mı?" - "Evet buna" bebek fiziği "diyoruz, bu bir basitleştirme. Bunu yapmanın doğru yolu bu".
DeusXMachina

102

Ayrıca her ne kadar beğendiğini işlev bir veri grubunu sığabilecek curve_fitdan scipy.optimize. Örneğin, üstel bir işleve sığdırmak istiyorsanız ( belgelerden ):

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)

Ve sonra çizmek istiyorsanız, şunları yapabilirsiniz:

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()

(Not: *önünde poptiçine şartlarını genişleyecektir çizmek zaman a, bve co func. Bekliyor)


2
Güzel. Ne kadar uygun olduğumuzu kontrol etmenin bir yolu var mı? R kare değeri? Daha iyi (veya daha hızlı) bir çözüm elde etmeye çalışabileceğiniz farklı optimizasyon algoritması parametreleri var mı?
user391339

Uyumun iyi olması için, takılmış optimize edilmiş parametreleri, scipy optimizasyon fonksiyonunun kareye atabilirsiniz; 2 değeri p değeri olan 2 değer döndürür.

Parametrelerini nasıl seçileceği ile ilgili herhangi bir fikir a, bve c?
I_told_you_so

47

Bu konuda biraz sorun yaşıyordum, bu yüzden benim gibi yeni başlayanların anlayabilmesi için çok açık olalım.

Diyelim ki bir veri dosyamız veya bunun gibi bir şey var

# -*- 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()

sonuç: a = 0.849195983017, b = -1.18101681765, c = 2.24061176543, d = 0.816643894816

Ham veriler ve takılı fonksiyon


8
y = [np.exp(i) for i in x]çok yavaş; numpy'nin yaratılmasının bir nedeni yazabilmenizdi y=np.exp(x). Ayrıca, bu değiştirme ile acımasız kuvvet bölümünüzden kurtulabilirsiniz. İpython'da %timeitsihir var In [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
14'te

1
Teşekkür ederim, haklısın, haklısın, ama yine de bu algoritmayı kullanarak karşılaştığım bir csv, xls veya diğer formatlardaki verilerle uğraşırken kullanmam gereken acımasız güç parçası. Bence bunun kullanımı sadece bir kişi deneysel veya simülasyon verilerinden bir fonksiyona uymaya çalıştığında anlamlıdır ve benim deneyimime göre bu veriler her zaman garip formatlarda gelir.
Leandro

3
x = np.array(x, dtype=float)yavaş liste kavrayışından kurtulmanızı sağlayacaktır.
Ajasja

8

Her zaman kullanabilirsiniz sanırım:

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

IanVS'nin cevabını biraz değiştirerek :

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()

Bu, aşağıdaki grafikle sonuçlanır:

resim açıklamasını buraya girin


Uyumun yaklaşık olarak doygunluk değeri var mı? Öyleyse, nasıl erişebilirim?
Ben

7

İşte basit veriler üzerinde scikit öğrenmenin araçlarını kullanan bir doğrusallaştırma seçeneği .

verilmiş

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)

kod

Üstel verileri sığdır

# 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")

resim açıklamasını buraya girin

Günlük verilerini sığdır

# 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")

resim açıklamasını buraya girin


ayrıntılar

Genel Adımlar

  1. Bir günlük veri değerlerine işlemi (uygulanır x, yya da her ikisi)
  2. Verileri doğrusallaştırılmış bir modele gerileme
  3. Herhangi bir günlük işlemini (ile np.exp()) "ters çevirerek" çizin ve orijinal verilere sığdırın

Verilerimizin üstel bir eğilim izlediği varsayıldığında, genel bir denklem + şu olabilir:

resim açıklamasını buraya girin

İkinci denklemi (örn. Y = kesişim + eğim * x) günlüğü alarak doğrusallaştırırız :

resim açıklamasını buraya girin

Doğrusallaştırılmış bir denklem ++ ve regresyon parametreleri göz önüne alındığında , şunları hesaplayabiliriz:

  • Aintercept ( ln(A)) aracılığıyla
  • Beğim ( B) ile

Doğrusallaştırma Tekniklerinin Özeti

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)

+ Not: Doğrusallaştırma üstel fonksiyonları gürültü en az ve C = 0 olduğunda en iyi sonucu verir. Dikkatle kullanın.

++ Not: x verilerini değiştirmek üstel verileri doğrusallaştırmaya yardımcı olurken , y verilerini değiştirmek günlük verilerini doğrusallaştırmaya yardımcı olur .


0

Her lmfitiki sorunu da çözmenin özelliklerini gösteriyoruz .

verilmiş

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

kod

Yaklaşım 1 - lmfitModel

Üstel verileri sığdır

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()

resim açıklamasını buraya girin

Yaklaşım 2 - Özel Model

Günlük verilerini sığdır

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()

resim açıklamasını buraya girin


ayrıntılar

  1. Bir regresyon sınıfı seçin
  2. Kaynağın adı, işlevin etki alanına saygı duyan ilk tahminler

Çıkarılan parametreleri regresör nesnesinden belirleyebilirsiniz. Misal:

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

Not: biri negatif olan iki parametreyi kabul eden ExponentialModel()bir bozunma fonksiyonunu takip eder.

resim açıklamasını buraya girin

Ayrıca bkz . Daha fazla parametreExponentialGaussianModel() kabul eder .

Kütüphaneyi yoluyla yükleyin> pip install lmfit .


0

Wolfram, üstel bir montaj için kapalı bir form çözümüne sahiptir . Ayrıca logaritmik ve güç yasası için benzer çözümlere sahiptirler .

Ben scipy's curve_fit daha iyi çalışmak için buldum. İşte bir örnek:

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()

resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.