pandas 在python中更改函数的默认参数

ymdaylpp  于 2023-03-11  发布在  Python
关注(0)|答案(3)|浏览(172)

我有一个函数,可以根据条件将numpy数组转换为包含True或False的数组,然后将相邻的True或False条目分组,并计算每组的长度,这是为了确定给定月份降雨量数据中干旱期或潮湿期的长度。
这是一个函数:

import itertools
def spell(X, kind='wet', how='mean', threshold=0.5): 

    if kind=='wet':
        condition = X>threshold
    else:
        condition = X<=threshold

    length = [sum(1 if x==True else nan for x in group) for key,group in itertools.groupby(condition) if key]

    if not length: 
        res = 0
    elif how=='mean': 
        res = np.mean(length)
    else:
        res = np.max(length)

    return res

因此,基本上有一个选项来确定平均长度或最大长度的湿或干期给定一个numpy数组的降水数据与默认参数设置为平均长度的湿期。
我对Pandas使用这个函数,将它应用到历史记录的每个月:

#Create example dataframe
np.random.seed(1324)
idx = pd.DatetimeIndex(start='1960-01-01', periods=100, freq='d')
values = np.random.random(100)
df = pd.DataFrame(values, index=idx)

#Apply function
df.resample('M', how=spell)

我得到的是

0
1960-01-31  1.555556
1960-02-29  1.500000
1960-03-31  1.777778
1960-04-30  6.000000

很完美,但是我希望能够动态地更改此函数的默认值,以便可以使用df的其他选项。resample()。我已经研究了functools.partial(),但是这只是输入参数显式设置的情况下的解决方案,即spell(kind='dry', how='max', threshold=0.7)。有没有办法改变函数的默认参数,而不需要在后面显式地设置它们,这样我就可以在df.resample()中使用它了?

0sgqnhkj

0sgqnhkj1#

函数的默认值存储在该函数的func_defaults属性中,该属性是与函数的func_code.co_varnames元组的 trailing 元素配对的值的元组。例如:

>>> def foo(x, y=5):
...    return x, y
...
>>> foo(10)
(10, 5)
>>> foo.func_code.co_varnames
('x', 'y')
>>> foo.func_defaults
(5,)
>>> foo.func_defaults = (7,)
>>> foo(10)
(10, 7)

您甚至可以在事后给予参数一个默认值:

>>> foo.func_defaults = (2, 3)
>>> foo()
(2, 3)

警告:我曾想过(ab)使用mock库来允许临时覆盖函数默认值,类似于recent answer of mine,然而,它似乎在之后将默认值设置为None,这意味着mock中存在bug(或者我误解了它的行为),或者像这样弄乱函数有点危险。

def foo(x=5):
    return x

assert foo() == 5
with mock.patch.object(foo, 'func_defaults', (10,)):
    assert foo() == 10

assert foo() == 5  # Oops; I'm observing foo.func_defaults to be None now

手动保存和恢复默认值似乎工作正常,不过,正如您所期望的那样。

orig_defaults = foo.func_defaults
foo.func_defaults = (10,)
assert foo() == 10
foo.func_defaults = orig_defaults
assert foo() == 5
vhmi4jdf

vhmi4jdf2#

这听起来像是函数 Package 器的工作!

def spellwrapper(newkind, newhow, newthreshold):
    def wrapped_spell_func(X):
        spell(X, kind=newkind, how=newhow, threshold=newthreshold)
    return wrapped_spell_func

可以使用以下命令调用此函数

new_spell_func = spellwrapper(newkind, newhow, newthreshold)

它将返回一个spell函数的 Package 版本,该函数使用新参数作为“默认值”,而不是在函数定义中创建的参数。

df.resample('M', how=new_spell_func)
vsdwdz23

vsdwdz233#

实际上,你可以使用partial函数来修改默认的关键字参数,这样就不需要在以后显式地设置它们了。诀窍是将原始函数设置为等于结果partial函数。

partial的一般示例

定义我们的泛型函数
def originalFunction(x,defaultValue='DEFAULT'):
    print('x:', x, 'defaultValue:', defaultValue,)
    
## shows the original function outputing x and the defaultValue
originalFunction(1)
x: 1 defaultValue: DEFAULT
使用partial覆盖函数签名(这两行是唯一重要的部分)

一个二个一个一个

要重置默认值,您可以重复调用partial
originalFunction = partial(originalFunction,defaultValue="whateverYouWant") ##sets default value

## Use function with updated defaults
originalFunction(3)
x: 3 defaultValue: whateverYouWant
或者您可以像这样修改分部函数的关键字字典
## Show dict containing keywords
originalFunction.keywords
{'defaultValue': 'whateverYouWant'}
originalFunction.keywords['defaultValue'] = "ThePossabilitesAreEndless" ##sets default value

## Use function with updated defaults
originalFunction("x")
x: x defaultValue: ThePossabilitesAreEndless
要恢复为原始kwargs,可以将变量覆盖为.func中的原始函数地址
originalFunction = originalFunction.func ##resets the default

## shows the original function outputing x and the defaultValue
originalFunction(3)
x: 3 defaultValue: DEFAULT

在原始问题中实施部分

显示函数“spell”的正常使用

一个12b1x一个13b1x

使用functools部分函数更新拼写kwargs
from functools import partial
spell = partial(spell,kind='dry', how='max', threshold=0.6)

#Apply function
print(df.resample('M').agg(lambda x: spell(x)).to_markdown())
dry max 0.6
dry max 0.6
dry max 0.6
dry max 0.6
|                     |   0 |
|:--------------------|----:|
| 1960-01-31 00:00:00 |   4 |
| 1960-02-29 00:00:00 |   5 |
| 1960-03-31 00:00:00 |   5 |
| 1960-04-30 00:00:00 |   2 |

使用函数工具与编辑__默认值__

在使用已导入的函数时,functools partial方法也很有用,因为模块中的函数不具有与当前python脚本中描述的函数类似的__defaults__函数
因为我们定义了originalFunction,所以可以使用.defaults访问默认的kwargs

x一个铝16块x一个铝17块x一个铝18块x一个铝19块x

此方法不适用于导入的模块,如np.array
np.array.__defaults__ ## module functions do not have __defaults__
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

~\AppData\Local\Temp/ipykernel_15784/3587101803.py in <module>
----> 1 np.array.__defaults__ ## module functions do not have __defaults__

AttributeError: 'builtin_function_or_method' object has no attribute '__defaults__'
所以我们可以使用partial函数来更新np.数组kwargs

一个22个1个23个1个1个24个1个1个1个25个1个1个

使用functools与在kwargs上传递

可以将args和kwargs传递给另一个函数,而不使用partial

x一个26块1 x一个27块1 x

我们可以将传入的参数和kwarg传递给另一个函数,同时使用以下方法覆盖defaultValue kwarg

一个28块一个

这种方法的一个缺点是,您不能使用这个新函数签名来覆盖原始函数!
## add display() to show infinite loop
def newFunction(*args,defaultValue='NEW_DEFAULT',**kwargs):
    display('Passing vals to OriginalFunction')
    return originalFunction(*args,defaultValue = defaultValue, **kwargs)
newFunction(5)
'Passing vals to OriginalFunction'

x: 5 defaultValue: NEW_DEFAULT
如果这样做,它将创建一个无限循环

一个32b1x一个33b1x

相关问题