幂律拟合在Python中不起作用:要么偏差严重,要么只返回初始参数

0 投票
1 回答
31 浏览
提问于 2025-04-14 16:07

我现在非常困惑。我想把一个幂律模型应用到我的数据上。我用随机生成的数据测试了我的代码,效果很好(见图),但是当我用自己的数据时,结果却完全不对。我尝试给拟合参数提供一些初始值来帮助 curve_fit,但在这种情况下,它只返回了我提供的初始参数。这让我很困惑。有没有人能帮帮我?

在这里输入图片描述

在这里输入图片描述

这是我的代码:

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

# Define the power law function
def power_law(x, factor, exponent):
    '''
    x: x axis data
    factor: y axis intersection
    exponent: slope
    '''
    return factor * x ** exponent


# Define the power-law function

# Generate synthetic data following a power-law distribution
np.random.seed(0)  # for reproducibility
x_data = np.linspace(1, 10, 50)  # example x values
y_data = power_law(x = x_data, factor = 0.2, exponent = -10) * (1 + np.random.normal(scale=0.1, size=len(x_data)))  # example y values with added noise

# Fit the power-law model to the data
params, covariance = curve_fit(power_law, x_data, y_data)

# Extract fitted parameters
fac_fit, exp_fit = params

# Plot the data and the fitted power-law curve
plt.figure()
plt.scatter(x_data, y_data, label = 'Data')
plt.plot(x_data, power_law(x_data, fac_fit, exp_fit), color='red', label='Fitted Power Law')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Fitting a Power Law to Data')
plt.legend()
plt.grid(True)
plt.show()

# Print the fitted parameters
print("Fitted Parameters:")
print("factor =", fac_fit)
print("exponent =", exp_fit)

# My data
x_data = freq_full_ordered_withM[mask]
y_data =  PSD_full_ordered_withM[mask]

# Filter out non-positive values from x_data and corresponding y_data
positive_mask = x_data > 0
x_data = x_data[positive_mask]
y_data = y_data[positive_mask]

# Fit the power-law model to the data
params, covariance = curve_fit(power_law, x_data, y_data)

# Extract fitted parameters
fac_fit, exp_fit = params

# Plot the data and the fitted power-law curve
plt.figure()
plt.scatter(x_data, y_data, label = 'Data')
plt.plot(x_data, power_law(x_data, fac_fit, exp_fit), color='red', label='Fitted Power Law')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Fitting a Power Law to Data')
plt.legend()
plt.grid(True)
plt.show()

更新:我给了一些初始参数,现在它开始拟合一些东西,但结果还是很不理想。

initial_guess = [10**2, -2]  # Initial guess parameters (a, b)
bounds = ([10**0, -5], [10**5, 0])  # Lower and upper bounds for parameters (a, b)

# Fit the power-law model to the data
params, covariance = curve_fit(power_law, x_data, y_data, p0=initial_guess, bounds=bounds)

在这里输入图片描述

1 个回答

2

为了让某些东西在十年范围内符合幂律,我觉得你可以尝试把y的对数值和x的对数值画成一条线。

这里的关系是:y = A.xn可以转化为log(y) = log(A) + n * log(x)。

下面这个方法在你的合成数据上效果比原来的要好,但我没有你的真实数据来试试。

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

# Define the power law function
def power_law(x, factor, exponent):
    return factor * x ** exponent

def linear_law(x, c, m):
    return m * x + c

# Generate synthetic data
np.random.seed(0)  # for reproducibility
x_data = np.linspace(1, 10, 50)        # example x values. NOTE: not well-distributed on a log scale
y_data = power_law(x = x_data, factor = 0.2, exponent = -10) * (1 + np.random.normal(scale=0.1, size=len(x_data)))  # example y values with added noise

params, covariance = curve_fit(linear_law, np.log( x_data ), np.log( y_data ) )

# Convert parameters back: y = Ax^n   <--->   log(y) = log(A) + n * log( x )
fac_fit, exp_fit = np.exp( params[0] ), params[1]

# Plot the data and the fitted power-law curve
plt.figure()
plt.scatter(x_data, y_data, label = 'Data')
plt.plot(x_data, power_law(x_data, fac_fit, exp_fit), color='red', label='Fitted Power Law')
plt.xscale('log');   plt.yscale('log')
plt.xlabel('X')  ;   plt.ylabel('Y')
plt.title('Fitting a Power Law to Data')
plt.legend()
plt.grid(True)
plt.show()

# Print the fitted parameters
print("Fitted Parameters:")
print("factor =", fac_fit)
print("exponent =", exp_fit)

输出结果:

Fitted Parameters:
factor = 0.2249726345273465
exponent = -10.07098332868395

在这里输入图片描述

撰写回答