Python:在scipy.optimize.newton中将列表作为参数传递

时间:2015-01-22 14:32:26

标签: python python-2.7 numpy scipy pythonxy

我想在optimize.newton中将列表作为参数传递。 我导入了一个csv并将每一行存储在一个数组中。这个代码如下:

with open('rand1.csv','rb') as f:
    array=[]
    for line in f:
        array.append(line)

现在,如果我查看array[1],它看起来像是:'2,6,76,45,78,1\r\n'

我已将一个函数定义为:

def func(a,b,c,d,e,f):
    return a*b*c-d*e-f

我正在运行牛顿方法:

res=[optimize.newton(func,5102,args=(x)) for x in array[0]]

但它给了我一个TypeError": can only concatenate tuple (not "str") to tuple"

有人可以帮助我吗?我知道元组元素必须以逗号分隔,我也尝试编写args=(x,),但它没有用。

2 个答案:

答案 0 :(得分:1)

首先,请记住,在您的代码中,array实际上并不是一个numpy数组 - 它是一个普通的Python list字符串。通过拆分字符串并将元素转换为整数,可以使用此列表,如Anmol_uppal的答案,但将csv文件的内容直接转换为更简单的方法要简单得多。 nrows x 6 numpy array,例如使用np.loadtxt

import numpy as np

data = np.loadtxt('rand1.csv', delimiter=',', dtype=np.int)
print(repr(data[0]))
# array([ 2,  6, 76, 45, 78,  1])

现在,当您致电optimize.newton时,args=参数应获得包含6个参数值的序列。您的原始代码无效,因为array中的每一行都包含一个字符串,而不是6个数字值。既然data *是一个nrows x 6数组,每行将包含6个数值,所以你现在可以这样做:

res = [optimize.newton(func, 5102, args=row) for row in data]

*请注意,我已将变量array重命名为data,以避免与np.array类混淆


更新

原始代码中还有一个我最初没有发现的错误。请查看scipy.optimize.newton的文档:

  

func:function

     
    

需要零的函数。它必须是f(x,a,b,c ...)形式的单个变量的函数,其中a,b,c ...是可以传入的额外参数 args参数。

  
     

x0:float

     
    

应该在实际零点附近的零的初始估计值。

  

现在看看你的函数定义:

def func(a,b,c,d,e,f):
    return a*b*c-d*e-f

func()第一个参数(您称之为a)应该与 x 参数对应,然后有只需 5 额外参数(根据您的定义b ... f)需要使用args=传递。当你试着打电话

optimize.newton(func, 5102, args=(422, 858, 129, 312, 79, 371))

所发生的是5102被解释为x0参数,并作为第一个参数传递给func()args=元组中的 6 值被视为额外参数,因此您的函数实际上总共得到 7 个参数:

func(5102, 422, 858, 129, 312, 79, 371)

显然,func()被定义为接受6个参数,因此您会收到错误。解决此问题的正确方法取决于您如何解释函数的参数。 newton的目标是找到 x 的值,使 f(x,a,b,c,...)= 0

您希望将{6}参数中的哪一个最小化func()


完整解释

一个稍微有趣的问题是,当您将额外参数作为数组(例如args=data[0])而不是元组传递时,为什么您不会收到错误。答案有点复杂,但如果您有兴趣,请继续阅读。

如果您查看the source code for scipy.optimize.newton,您可以找到第一次调用函数的行:

q0 = func(*((p0,) + args))

在这种情况下,p0p1将成为x0的{​​{1}}参数,newton()是额外参数的集合:

args

q0 = func(*((5102,) + (422, 858, 129, 312, 79, 371))) 是一个元组,如果(p0,)也是一个元组,那么args运算符就会将这两个元组连接在一起:

+

最后,q0 = func(*(5102, 422, 858, 129, 312, 79, 371)) 解包元组以将参数传递给*。最后的电话会是这样的:

func

这会引发错误,因为6参数函数有7个参数。但是,当q0 = func(5102, 422, 858, 129, 312, 79, 371) args

np.array

q0 = func(*(5102,) + array([422, 858, 129, 312, 79, 371])) +添加到p0中的每个元素:

args

由于现在只有 6 个参数转到q0 = func(*(5524, 5960, 5231, 5414, 5181, 5473)) ,调用才会成功,但func()会收到错误答案!

我认为这在scipy中并不是特别好的设计 - 它让我感到高兴,因为在大多数其他情况下,任何类似数组的输入都会做,包括列表,元组,数组等。公平地说,它确实在文档中说newton newton应该是一个元组,但我仍然会进行类型检查或将其明确地转换为元组以确保安全。我可以尝试在scipy中解决这个问题。

答案 1 :(得分:0)

首先你需要删除那个尾随'\r', '\t'并为此你可以使用.strip(),现在你有一个字符串,其中所需的元素由逗号分隔,在这里你可以使用{{ 1}}方法并传递要在给定字符串上拆分的字符。最后我们使用了map()函数,它接受一个函数作为第一个参数(在本例中为int),第二个参数是一个列表或元组,并将该元组列表的每个元素映射到作为第一个参数传递的函数。

.split()

结合上述所有步骤,我们可以将其简洁地写为:

line = '2,6,76,45,78,1\r\n'
line_stripped = line.strip()
print line_stripped
>>> '2,6,76,45,78,1'

line_splitted = line_stripped.split(",")
print line_splitted
>>> '2' ,'6', '76', '45', '78', '1'

line_integers = map(int,line_splitted)
print line_integers
>>> [2, 6, 76, 45, 78, 1]