Python 更改numpy数组中一致值的比例
我有个问题一直在想。假设我有一个如下所示的numpy数组(在实际实现中,Python 更改numpy数组中一致值的比例,python,arrays,numpy,Python,Arrays,Numpy,我有个问题一直在想。假设我有一个如下所示的numpy数组(在实际实现中,len(array)大约为4500): 由此,我尝试生成一个新的(无序)数组,其中随机符合array的值的比例是一个特定的比例p。那么让我们假设p=.5。然后,一个新的数组示例如下 array = [0, 0, 1, 1, 2, 2] new_array = [0, 1, 2, 1, 0, 2] 您可以看到new_array中正好有50%的值与array中的值一致。输出阵列的要求如下: np.count\u非零(数
len(array)
大约为4500):
由此,我尝试生成一个新的(无序)数组,其中随机符合array
的值的比例是一个特定的比例p
。那么让我们假设p=.5
。然后,一个新的数组示例如下
array = [0, 0, 1, 1, 2, 2]
new_array = [0, 1, 2, 1, 0, 2]
您可以看到new_array
中正好有50%的值与array
中的值一致。输出阵列的要求如下:
np.count\u非零(数组-新数组)/len(数组)=p
,和set(np.unique(数组))==set(np.unique(新数组))
我所说的“同意”是指同意索引的array[I]==new\u array[I]
。new_数组
中的所有值应与数组
中的值相同,只是乱序而已
我相信有一种优雅的方式可以做到这一点——有人能想出什么吗
谢谢 您可以尝试以下方法
array = [0, 0, 1, 1, 2, 2]
new_array = [0, 1, 2, 1, 0, 2]
随机导入
p=0.5
arr=np.数组([0,0,1,1,2,2])
#所需的类似元件数量
num_sim_元素=圆形(len(arr)*p)
#创建相似元素的索引
hp={}
对于枚举中的i,e(arr):
如果(hp中的e):
hp[e].附加(i)
其他:
hp[e]=[i]
#打印(hp)
out_map=[]
k=列表(hp.keys())
v=列表(hp.values())
索引=0
while(len(out\u map)!=num\u sim\u元素):
如果(len(v[index])>0:
k_uk=k[索引]
随机。随机(v[索引])
v_uv=v[index].pop()
out_map.append((k_,v_))
指数+=1
指数%=len(k)
#打印(输出地图)
out\u unique=set([i[0]表示i in-out\u映射])
out_索引=[i[-1]表示i in-out_映射]
out_arr=arr.copy()
#对于输入输出图:
#out_arr[i[-1]]=i[0]
对于集合中的i(范围(len(arr))。差异(out_指数):
out\u arr[i]=random.choice(列表(out\u unique.difference([out\u arr[i]]))
打印(arr)
打印(输出)
断言1-(np.count\u nonzero(arr-out\u arr)/len(arr))==p
断言集(np.unique(arr))==集(np.unique(out_arr))
以下是一个可能更容易理解的版本:
import math, random
# generate array of random values
a = np.random.rand(4500)
# make a utility list of every position in that array, and shuffle it
indices = [i for i in range(0, len(a))]
random.shuffle(indices)
# set the proportion you want to keep the same
proportion = 0.5
# make two lists of indices, the ones that stay the same and the ones that get shuffled
anchors = indices[0:math.floor(len(a)*proportion)]
not_anchors = indices[math.floor(len(a)*proportion):]
# get values of non-anchor indices, and shuffle them
not_anchor_values = [a[i] for i in not_anchors]
random.shuffle(not_anchor_values)
# loop original array, if an anchor position, keep original value
# if not an anchor, draw value from shuffle non-anchor value list and increment the count
final_list = []
count = 0
for e,i in enumerate(a):
if e in not_anchors:
final_list.append(i)
else:
final_list.append(not_anchor_values[count])
count +=1
# test proportion of matches and non-matches in output
match = []
not_match = []
for e,i in enumerate(a):
if i == final_list[e]:
match.append(True)
else:
not_match.append(True)
len(match)/(len(match)+len(not_match))
代码中的注释解释了该方法。(编辑了,以包含不同且更准确的方法)
需要注意的是,并非所有的混洗分数p
(混洗元素数除以元素总数)的值都是可访问的。
p
的可能值取决于输入的大小和重复元素的数量
尽管如此,我可以提出两种可能的方法:
将numpy导入为np
def部分_混洗(arr,p=1.0):
n=阵列尺寸
k=圆形(n*p)
洗牌=np.arange(n)
洗牌=np.random.choice(n,k,replace=False)
洗牌[洗牌]=np.排序(洗牌)
返回arr[洗牌]
方法(1)的主要优点是,它可以很容易地以矢量化的形式实现,并使用高级索引。
另一方面,只要您愿意接受由于重复值或仅仅因为混洗索引意外地与未混洗的索引重合,某些混洗可能会返回一些未混洗的元素,这一点就行得通。
这会导致p
的请求值通常大于观察到的实际值。
如果需要相对更精确的p
,可以尝试搜索p
参数,在输出上给出所需的值,或者进行反复试验
def partial_shuffle_eff(arr,p=1.0,inplace=False,trys=2.0):
如果不到位:
arr=arr.copy()
n=阵列尺寸
k=圆形(n*p)
尝试=圆形(n*次尝试)
seen=set()
i=l=t=0
当i
虽然这种方法得到了更准确的p
,但它仍然受到以下事实的限制:掉期的目标数量必须是偶数。
此外,对于具有大量唯一性的输入,第二个while
(while j in seen…
)可能是一个无限循环,因此应设置尝试次数的上限。
最后,您需要使用显式循环,从而导致执行速度大大降低,除非您可以使用Numba的JIT编译,这将显著加快执行速度
import numba as nb
partial_shuffle_eff_nb = nb.njit(partial_shuffle_eff)
partial_shuffle_eff_nb.__name__ = 'partial_shuffle_eff_nb'
为了测试部分洗牌的准确性,我们可以使用(百分比):
def-hamming_距离(a,b):
断言(a.shape==b.shape)
返回np.count_非零(a==b)
def哈明距离百分比(a,b):
返回汉明顿距离(a,b)/len(a)
def混洗分数(a、b):
返回1%的哈明距离(a,b)
我们可以观察到类似的行为:
funcs = (
partial_shuffle,
partial_shuffle_eff,
partial_shuffle_eff_nb
)
n = 12
m = 3
arrs = (
np.zeros(n, dtype=int),
np.arange(n),
np.repeat(np.arange(m), n // m),
np.repeat(np.arange(3), 2),
np.repeat(np.arange(3), 3),
)
np.random.seed(0)
for arr in arrs:
print(" " * 24, arr)
for func in funcs:
shuffled = func(arr, 0.5)
print(f"{func.__name__:>24s}", shuffled, shuffling_fraction(arr, shuffled))
# [0 0 0 0 0 0 0 0 0 0 0 0]
# partial_shuffle [0 0 0 0 0 0 0 0 0 0 0 0] 0.0
# partial_shuffle_eff [0 0 0 0 0 0 0 0 0 0 0 0] 0.0
# partial_shuffle_eff_nb [0 0 0 0 0 0 0 0 0 0 0 0] 0.0
# [ 0 1 2 3 4 5 6 7 8 9 10 11]
# partial_shuffle [ 0 8 2 3 6 5 7 4 9 1 10 11] 0.5
# partial_shuffle_eff [ 3 8 11 0 4 5 6 7 1 9 10 2] 0.5
# partial_shuffle_eff_nb [ 9 10 11 3 4 5 6 7 8 0 1 2] 0.5
# [0 0 0 0 1 1 1 1 2 2 2 2]
# partial_shuffle [0 0 2 0 1 2 1 1 2 2 1 0] 0.33333333333333337
# partial_shuffle_eff [1 1 1 0 0 1 0 0 2 2 2 2] 0.5
# partial_shuffle_eff_nb [1 2 1 0 1 0 0 1 0 2 2 2] 0.5
# [0 0 1 1 2 2]
# partial_shuffle [0 0 1 1 2 2] 0.0
# partial_shuffle_eff [1 1 0 0 2 2] 0.6666666666666667
# partial_shuffle_eff_nb [1 2 0 1 0 2] 0.6666666666666667
# [0 0 0 1 1 1 2 2 2]
# partial_shuffle [0 0 1 1 0 1 2 2 2] 0.2222222222222222
# partial_shuffle_eff [0 1 2 1 0 1 2 2 0] 0.4444444444444444
# partial_shuffle_eff_nb [0 0 1 0 2 1 2 1 2] 0.4444444444444444
或者,对于更接近您的用例的输入:
n = 4500
m = 3
arr = np.repeat(np.arange(m), n // m)
np.random.seed(0)
for func in funcs:
shuffled = func(arr, 0.5)
print(f"{func.__name__:>24s}", shuffling_fraction(arr, shuffled))
# partial_shuffle 0.33777777777777773
# partial_shuffle_eff 0.5
# partial_shuffle_eff_nb 0.5
最后是一些小型基准测试:
n = 4500
m = 3
arr = np.repeat(np.arange(m), n // m)
np.random.seed(0)
for func in funcs:
print(f"{func.__name__:>24s}", end=" ")
%timeit func(arr, 0.5)
# partial_shuffle 213 µs ± 6.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# partial_shuffle_eff 10.9 ms ± 194 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# partial_shuffle_eff_nb 172 µs ± 1.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
基于索引的同意?你所说的同意是什么意思?这有点含糊不清。@wwii中的“同意”,我的意思是
array[I]==new_array[I]
@Polkaguy6000是的。这很有趣,但我看到了一个问题,如果你有数组[1,1,1,2]
和所需的匹配比.25,你的输出不可能是[2,1,1,1]
(a.5匹配)或[1,1,2]
(1.0匹配)。Sor
n = 4500
m = 3
arr = np.repeat(np.arange(m), n // m)
np.random.seed(0)
for func in funcs:
print(f"{func.__name__:>24s}", end=" ")
%timeit func(arr, 0.5)
# partial_shuffle 213 µs ± 6.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# partial_shuffle_eff 10.9 ms ± 194 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# partial_shuffle_eff_nb 172 µs ± 1.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)