【问题标题】:Python:Passing list as argument in scipy.optimize.newtonPython:在 scipy.optimize.newton 中将列表作为参数传递
【发布时间】:2015-01-22 14:32:25
【问题描述】:

我想将列表作为参数传递给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,),但没有成功。

【问题讨论】:

  • 首先strip() 行(在追加之前)并使用.split(',') 方法

标签: python python-2.7 numpy scipy pythonxy


【解决方案1】:

首先,请记住,在您的代码中,array 实际上并不是一个 numpy 数组 - 它是一个普通的 Python list 字符串。可以通过拆分字符串并将元素转换为整数来使用此列表,如 Anmol_uppal 的回答,但将 csv 文件的内容直接转换为 nrows x 6 numpy 数组要简单得多,例如使用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 的文档:

函数:函数

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

x0 : 浮动

对零的初始估计应该接近实际零。

现在看看你的函数定义:

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

func()(你称之为a)的first参数应该对应x参数,那么只有5 需要使用args= 传递的额外参数(b ... f 根据您的定义)。当你尝试打电话时

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

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


完整解释

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

如果你看一下the source code for scipy.optimize.newton,你会发现你的函数第一次被调用的那一行:

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

在这种情况下,p0p1 将是 x0 的参数 newton()args 是一组额外的参数:

q0 = func(*((5102,) + (422, 858, 129, 312, 79, 371)))

(p0,) 是一个元组,如果args 也是一个元组,那么+ 运算符只会将这两个元组连接在一起:

q0 = func(*(5102, 422, 858, 129, 312, 79, 371))

最后,* 解包元组以将参数传递给func。最终调用如下所示:

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

这将引发错误,因为 6 参数函数有 7 个参数。但是,当argsnp.array 时:

q0 = func(*(5102,) +  array([422, 858, 129, 312, 79, 371]))

+ 会将值p0添加args 中的每个元素:

q0 = func(*(5524, 5960, 5231, 5414, 5181, 5473))

由于现在只有 6 个参数传递给func(),因此调用会成功,但newton 会收敛到错误的答案!

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

【讨论】:

  • 谢谢,这稍微解决了我的问题。但我在这里还有一些疑问。如果我在具有 8 行和 6 列的 csv 上运行您的代码,它工作正常。如果我是正确的,形成的数组将是 8x6。 'res' 变量将有 8 个值。但是如果我只想为第一行运行优化代码怎么办?我无法运行这个:res = [optimize.newton(func, 5102, args=row) for row in data[0]]。此外,如果我通过手动指定值来运行代码,则会为 res 提供不同的值。请帮忙。
  • 如果您只想对第一行进行优化,您可以使用res0 = optimize.newton(func, 5102, args=data[0]),其中res0 将是单个标量而不是列表。如果要检查数组是否具有预期的尺寸,请检查 data.shape 属性的值。当您说手动指定值时得到不同的结果时,我不知道您的意思。也许您在数组中索引了错误的行?请记住,在 Python 中,索引从 0 而不是 1 开始。
  • 我知道 Python 中的索引是从 0 开始的。这是这种情况。数据 [0] 看起来像:'array([422, 858, 129, 312, 79, 371])'。当我运行代码“optimize.newton(func, 5102, args=data[0])”时,答案是-129,而如果我运行与“optimize.newton(func, 5102, args=(422, 858, 129, 312, 79, 371))”,它给了我一个错误。
  • 我正在等待您对此的回复。非常感谢您的帮助。
  • @PrateekSaxena 我已经修改了我的答案 - 您的代码中还有一个错误最初对我来说并不明显,因为(我认为是)optimize.newton 中的一个缺陷。完整的解释有点长,但重要的一点是 1) 您向 func() 传递了太多参数,以及 2) args= 参数实际上应该是一个元组而不是一个数组(例如 args=tuple(data[0])) .
【解决方案2】:

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

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]

结合以上所有步骤,我们可以干净地写成:

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

【讨论】:

  • 这帮助我摆脱了 '\r'、'\t' 但我仍然无法运行我的 optimize.newton 函数。这里有什么建议吗?
猜你喜欢
  • 2011-01-20
  • 2021-06-01
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
  • 2013-11-13
  • 2015-04-17
  • 1970-01-01
相关资源
最近更新 更多