如何生成可以求和为特定值的随机数?

how to generate random numbers that can be summed to a specific value?

我有 2 个数据框如下:

import pandas as pd
import numpy as np
# Create data set.
dataSet1 = {'id': ['A', 'B', 'C'],
           'value' : [9,20,20]}
dataSet2 = {'id' : ['A', 'A','A','B','B','B','C'],
            'id_2': [1, 2, 3, 2,3,4,1]}
# Create dataframe with data set and named columns.
df_map1 = pd.DataFrame(dataSet1, columns= ['id', 'value'])

df_map2 = pd.DataFrame(dataSet2, columns= ['id','id_2'])

df_map1

    id  value
0   A   9
1   B   20
2   C   20

df_map2

    id  id_2
0   A   1
1   A   2
2   A   3
3   B   2
4   B   3
5   B   4
6   C   1

其中 id_2 可以有重复的 id。 (即 id_2 是 id 的子集)

#doing a quick merge, based on id.
df = df_map1.merge(df_map2 ,on=['id'])
    id  value   id_2
0   A   9         1
1   A   9         2
2   A   9         3
3   B   20        2
4   B   20        3
5   B   20        4
6   C   20        1

我可以表示id和id_2的关系如下

id_ref = df.groupby('id')['id_2'].apply(list).to_dict()
{'A': [1, 2, 3], 'B': [2, 3, 4], 'C': [1]}

现在,我想生成随机整数,比如 0 到 3 将列表(例如 5 个元素)放入 pandas df 并展开。

import numpy as np
import random
df['random_value'] = df.apply(lambda _: np.random.randint(0,3, 5), axis=1)

    id  value   id_2        random_value
0   A   9        1        [0, 0, 0, 0, 1]
1   A   9        2        [0, 2, 1, 2, 1]
2   A   9        3        [0, 1, 2, 2, 1]
3   B   20       2        [2, 1, 1, 2, 2]
4   B   20       3        [0, 0, 0, 0, 0]
5   B   20       4        [1, 0, 0, 1, 0]
6   C   20       1        [1, 2, 2, 2, 1]

生成此 random_value 列表的条件是列表的总和必须等于 9。

这意味着,对于 id : A,如果我们将列表中的所有元素相加,我们总共有 13 个,如下所述,但我们想要的是 9:

id B 和 C 的相同概念......等等....

有没有办法实现这个?

# i was looking into multinomial from np.random function... seems this should be the solution but im not sure how to apply this with pandas.

np.random.multinomial(9, np.ones(5)/5, size = 1)[0]

=> array([2,3,3,0,1])

2+3+3+0+1 = 9

ATTEMPT/IDEA ...

鉴于我们有 id_2 的列表。 ie) id: A 有 3 个不同的元素 [1,2,3].

所以 id A 被映射到 3 个不同的元素。所以我们可以得到

3 * 5 = 15(这将是我们的长列表)

3: 列表长度

5: 创建列表的 5 个元素

因此

list_A = np.random.multinomial(9,np.ones(3*5)/(3*5) ,size = 1)[0]

然后我们均匀 distribute/split 列表。 使用此列表理解:

[list_A [i:i + n] for i in range(0, len(list_A ), n)]

但我仍然不确定如何动态执行此操作。

核心思想如您所说(关于获得 3*5=15 个数字),再加上将其重塑为一个二维数组,其行数与 id 在数据框中的行数相同。下面的函数就是这样做的,

def generate_random_numbers(df):
    value = df['value'].iloc[0]

    list_len = 5
    num_rows = len(df)
    num_rand = list_len*num_rows
    
    return pd.Series(
        map(list, np.random.multinomial(value, np.ones(num_rand)/num_rand).reshape(num_rows, -1)),
        df.index
    )

并应用它:

df['random_value'] = df.groupby(['id', 'value'], as_index=False).apply(generate_random_numbers).droplevel(0)