我正在寻找一种优雅(或更优雅)的方式来编写numpy中的特定用例。用例是一个大型数据集(因此效率很重要),包含100多个字段,超过1,000行代码和多个代码段,我只想处理这些字段的子集。只要我正在处理所有的观察结果,这在简单的numpy中是干净而有效的:
wages = np.arange(40000,60000,2000)
cg = np.arange(0,100000,10000)
ded = np.repeat([6000,9000],5)
exem = np.repeat([2000,4000],5)
agi = wages + cg
tinc = agi - ded
tinc = tinc - exem
但是在许多代码小节中,我只想处理30行代码的观察的子集,这是我能想到的最好的:
agi = wages + cg
mask = wages < 50001
tinc = agi
tinc[mask] = agi[mask] - ded[mask]
tinc[mask] = tinc[mask] - exem[mask]
这并不可怕,不要误解我的意思,而是将这个变量与数百行代码相乘。有没有办法做以下的事情,而不诉诸cython / numba循环?
# fake code, just to convey the desired syntax
agi = wages + cg
tinc = agi
mask( wages < 50001 ): # i.e. inside a python loop, would be "if wages < 50001:"
tinc = agi - ded
tinc = tinc - exem
换句话说,我想定义代码的子部分,并指定完全相同的掩码应该应用于代码部分中的每个单独的数组,而不是为每个单独的数组显式键入掩码。
(顺便说一下,我知道可能有一些通过熊猫的替代方法,但现在我更愿意通过numpy来探索我的最佳选择。我可能会在稍后用pandas标签重新提出这个问题。)
答案 0 :(得分:1)
我不推荐这个,但是......你可以用一个可怕的魔术上下文管理器来做这件事。例如:
@contextlib.contextmanager
def masking(namespace, mask):
# If you don't have a fixed set of maskable variables, make it
# an instance/global/local variables, like `_names`, or just
# [name for name, value in namespace.items() if isiinstance(value, np.ndarray)]
names = 'tinc agi dec exem'.split()
stash = {name: namespace[name] for name in names}
for name in names:
namespace[name] = namespace[name][mask]
try:
yield
finally:
for name in names:
namespace[name] = stash[name]
现在你可以这样做:
with masking(globals(), wages < 50001):
tinc = agi - dec
tinc = tinc - exem
with masking(self.__dict__, self.wages < 50001):
self.tinc = self.agi - self.dec
self.tinc = self.tinc - self.exem
# etc.
答案 1 :(得分:0)
执行此操作的一种方法可以使用Numpy's masked arrays。
Numpy的文档进一步详细介绍,我建议在开始之前仔细查看。它可能会增加一些复杂性(你可能需要或不需要使用function under numpy.ma
),你必须要小心你的代码中的所有操作仍然是正确的,但如果你使用蒙面数组你可以轻松定义这样的上下文管理器:
@contextlib.contextmanager
def mask(clause, masked_arrays):
for ma in masked_arrays:
ma[~clause] = np.ma.masked
yield
for ma in masked_arrays:
ma.mask = np.ma.nomask
请注意,由于屏蔽操作必须就地,因此需要使用的数组是屏蔽数组。然后你可以轻松地使用它:
# Wrap arrays as masked arrays
mwages = np.ma.array(wages)
mcg = np.ma.array(cg)
... etc ...
with mask(mwages < 50001, [mwages, mcg, mded, ...]):
# np.ma.compressed returns 1D array of non-masked items
unmasked_tinc = np.ma.compressed(magi - mded - mexem)
# or must change the masked one directly