我有以下代码来解决非负最小二乘法。 使用scipy.nnls.
import numpy as np
from scipy.optimize import nnls
A = np.array([[60, 90, 120],
[30, 120, 90]])
b = np.array([67.5, 60])
x, rnorm = nnls(A,b)
print x
#[ 0. 0.17857143 0.42857143]
# Now need to have this array sum to 1.
我想要做的是对x
解决方案应用约束,使其总和为1.我该怎么做?
答案 0 :(得分:9)
我认为您不能直接使用nnls
,因为它调用的Fortran code不允许额外的约束。但是,方程总和为1的约束可以作为第三个等式引入,因此您的示例系统具有以下形式,
60 x1 + 90 x2 + 120 x3 = 67.5
30 x1 + 120 x2 + 90 x3 = 60
x1 + x2 + x3 = 1
由于这是一组线性方程,因此可以从x=np.dot(np.linalg.inv(A),b)
获得精确解,以便x=[0.6875, 0.3750, -0.0625]
。这需要x3
为否定。因此,当x
对此问题有积极作用时,没有确切的解决方案。
对于x
被约束为正的近似解,可以使用
import numpy as np
from scipy.optimize import nnls
#Define minimisation function
def fn(x, A, b):
return np.sum(A*x,1) - b
#Define problem
A = np.array([[60., 90., 120.],
[30., 120., 90.],
[1., 1., 1. ]])
b = np.array([67.5, 60., 1.])
x, rnorm = nnls(A,b)
print(x,x.sum(),fn(x,A,b))
,x=[0.60003332, 0.34998889, 0.]
为x.sum()=0.95
。
我认为如果你想要一个更通用的解决方案,包括求和约束,你需要在下面的形式中使用带有显式约束/边界的最小化,
import numpy as np
from scipy.optimize import minimize
from scipy.optimize import nnls
#Define problem
A = np.array([[60, 90, 120],
[30, 120, 90]])
b = np.array([67.5, 60])
#Use nnls to get initial guess
x0, rnorm = nnls(A,b)
#Define minimisation function
def fn(x, A, b):
return np.linalg.norm(A.dot(x) - b)
#Define constraints and bounds
cons = {'type': 'eq', 'fun': lambda x: np.sum(x)-1}
bounds = [[0., None],[0., None],[0., None]]
#Call minimisation subject to these values
minout = minimize(fn, x0, args=(A, b), method='SLSQP',bounds=bounds,constraints=cons)
x = minout.x
print(x,x.sum(),fn(x,A,b))
提供x=[0.674999366, 0.325000634, 0.]
和x.sum()=1
。从最小化开始,总和是正确的,x
的值np.dot(A,x)=[ 69.75001902, 59.25005706]
并不完全正确。