使用重复的数字和替代方法有效地使用numpy.random.choice

时间:2016-09-01 09:37:36

标签: python python-2.7 numpy casting repeat

我需要生成一个包含重复元素的大型数组,我的代码是:

np.repeat(xrange(x,y), data)

但是,数据是一个类型为float64的numpy数组(但它表示integeres,没有2.1),我得到错误

TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'

例:

In [35]: x
Out[35]: 26

In [36]: y
Out[36]: 50

In [37]: data
Out[37]: 
array([ 3269.,   106.,  5533.,   317.,  1512.,   208.,   502.,   919.,
     406.,   421.,  1690.,  2236.,   705.,   505.,   230.,   213.,
     307.,  1628.,  4389.,  1491.,   355.,   103.,   854.,   424.])
In [38]: np.repeat(xrange(x,y), data)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call    last)
<ipython-input-38-105860821359> in <module>()
----> 1 np.repeat(xrange(x,y), data)

/home/pcadmin/anaconda2/lib/python2.7/site-packages/numpy    /core/fromnumeric.pyc in repeat(a, repeats, axis)
394         repeat = a.repeat
395     except AttributeError:
--> 396         return _wrapit(a, 'repeat', repeats, axis)
397     return repeat(repeats, axis)
398 

/home/pcadmin/anaconda2/lib/python2.7/site-packages/numpy  /core/fromnumeric.pyc in _wrapit(obj, method, *args, **kwds)
 46     except AttributeError:
 47         wrap = None
---> 48     result = getattr(asarray(obj), method)(*args, **kwds)
 49     if wrap:
 50         if not isinstance(result, mu.ndarray):

TypeError: Cannot cast array data from dtype('float64') to dtype('int64') according to the rule 'safe'

我通过将代码更改为

来解决它
np.repeat(xrange(x,y), data.astype('int64'))

然而,这是我的代码中最昂贵的行之一!!还有另一种选择吗?

顺便说一句,我在里面使用了这个

np.random.choice(np.repeat(xrange(x,y), data.astype('int64')), z)

为了获得样本而不用x和y之间的整数的大小z替换,并且在数据中给出每个的数量。我想这也是最好的方法呢?

3 个答案:

答案 0 :(得分:4)

问题陈述

这一个非常有趣的问题!只是为了让读者在不涉及次要数据转换问题的情况下了解问题,我们有一系列的值,让我们说a = np.arange(5),即

a = np.array([0,1,2,3,4])

现在,让我们说我们有另一个数组,列出了5中每个a数字的重复次数。那么,让那些:

reps = np.array([2,4,6,2,2])

接下来,我们正在执行这些重复:

In [32]: rep_nums = np.repeat(a,reps)

In [33]: rep_nums
Out[33]: array([0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4])

最后,我们希望使用z并且无需替换,从这些重复数字中选择np.random.choice()个元素。

让我们说z = 7选择7元素,所以np.random.choice(),我们会:{/ p>

In [34]: np.random.choice(rep_nums,7,replace=False)
Out[34]: array([2, 4, 0, 2, 4, 1, 2])

现在,这个without replacement这个术语可能听起来很混乱,因为我们已经在rep_nums中重复了这些数字。但是,它本质上意味着,np.random.choice()的输出不能包含例如4's。超过两个rep_nums,因为4's有两个np.repeat

所以,问题是我们想要摆脱rep_nums部分,这可能是真正巨大数组的瓶颈。

提议的方法

查看z = 7的输出,一个想法是生成rep_nums范围内的In [44]: np.random.choice(rep_nums.size,7,replace=False) Out[44]: array([ 7, 2, 4, 10, 13, 8, 3]) 个唯一元素:

5

这些数字代表该长度的指数。因此,我们只需要在rep_nums中查找7个分区中的bin(np.searchsorted个分区),其中每个x数字都会进入。为此,我们可以使用{{ 1}}。因此,我们将有一个实现来处理泛型y# Get the intervals of those bins intervals = data.astype(int).cumsum() # Decide length of array if we had repeated with `np.repeat` max_num = intervals[-1] # Get unique numbers (indices in this case) ids = np.random.choice(max_num,z,replace=False) # Use searchsorted to get bin IDs and add in `x` offset out = x+np.searchsorted(intervals,ids,'right') ,就像这样 -

def org_app(x,y,z,data):
    rep_nums = np.repeat(range(x,y), data.astype('int64'))
    out = np.random.choice(rep_nums, z,replace=False)
    return out

def optimized_v1(x,y,z,data):     
    intervals = data.astype(int).cumsum()
    max_num = intervals[-1]
    ids = np.random.choice(max_num,z,replace=False)
    out = x+np.searchsorted(intervals,ids,'right')
    return out

运行时测试

功能:

In [79]: # Setup inputs
    ...: x = 100
    ...: y = 10010
    ...: z = 1000
    ...: data = np.random.randint(100,5000,(y-x)).astype(float)
    ...: 

In [80]: %timeit org_app(x,y,z,data)
1 loop, best of 3: 7.17 s per loop

In [81]: %timeit optimized_v1(x,y,z,data)
1 loop, best of 3: 6.92 s per loop

关于全部职能的时间 -

np.repeat

看起来我们的速度并不快。让我们深入挖掘一下,找出我们在替换In [82]: %timeit np.repeat(range(x,y), data.astype('int64')) 1 loop, best of 3: 227 ms per loop 时节省了多少钱!

首先关闭原始方法 -

np.random.choice()

让我们看看我们采用所提出的方法对此有多大改进。所以,让我们在提议的方法中除In [83]: intervals = data.astype(int).cumsum() ...: max_num = intervals[-1] ...: ids = np.random.choice(max_num,z,replace=False) ...: out = x+np.searchsorted(intervals,ids,'right') ...: In [84]: %timeit data.astype(int).cumsum() 10000 loops, best of 3: 36.6 µs per loop In [85]: %timeit intervals[-1] 10000000 loops, best of 3: 142 ns per loop In [86]: %timeit x+np.searchsorted(intervals,ids,'right') 10000 loops, best of 3: 127 µs per loop 之外的所有内容 -

227ms

这比np.repeat的{​​{1}}要好得多!!

所以,我们希望在真正庞大的数组中,删除np.repeat的好处真的会大放异彩,否则np.random.choice()本身就会成为瓶颈。

答案 1 :(得分:4)

潜伏在问题中的是multivariate hypergeometric distribution。在Numpy drawing from urn中,我实现了一个从此分布中提取样本的函数。我怀疑它与答案中描述的@DiogoSantos解决方案非常相似。 Diogo说使用这种方法很慢,但我发现以下内容比Divakar optmized_v1要快。

这是一个函数,它使用链接答案中的sample(n, colors)来实现与Divakar函数具有相同签名的函数。

def hypergeom_version(x, y, z, data):
    s = sample(z, data)
    result = np.repeat(np.arange(x, y), s)
    return result

(这将返回排序顺序中的值。如果您需要以随机顺序排列值,请在return语句之前添加np.random.shuffle(result)。它不会显着改变执行时间。 )

比较:

In [153]: x = 100

In [154]: y = 100100

In [155]: z = 10000

In [156]: data = np.random.randint(1, 125, (y-x)).astype(float)

Divakar的optimized_v1

In [157]: %timeit optimized_v1(x, y, z, data)
1 loop, best of 3: 520 ms per loop

hypergeom_version

In [158]: %timeit hypergeom_version(x, y, z, data)
1 loop, best of 3: 244 ms per loop

如果data中的值较大,则相对性能会更好:

In [164]: data = np.random.randint(100, 500, (y-x)).astype(float)

In [165]: %timeit optimized_v1(x, y, z, data)
1 loop, best of 3: 2.91 s per loop

In [166]: %timeit hypergeom_version(x, y, z, data)
1 loop, best of 3: 246 ms per loop

答案 2 :(得分:1)

为了完成,我还有一个替代实现。鉴于我们有value_unit,我们可以为每个类使用超几何采样:

  • 计算反向data
  • 为每个类绘制data.cumsum()

然而,当我们有很多班级,每个班级都很少,这需要很长时间。