python 根据统计信息重新创建列表

llmtgqce  于 2023-01-11  发布在  Python
关注(0)|答案(1)|浏览(103)

我得到了一个数组的以下统计信息:

  • 长度
  • 最小值
  • 最大值
  • 平均值
  • 中位数
  • 夸蒂莱

我应该用或多或少相同的统计数据重新创建一个列表。我知道计算统计数据的列表不是正态分布的。
我的第一个想法是通过创建一个给定范围内的随机数列表来强制执行,并希望其中一个能适合。这种方法的好处是它能工作,而缺点显然是效率。
所以我正在寻找一个更有效的方法来解决这个问题。希望有人能帮助...
目前我只使用麻木,但我不限于它。
编辑1:作为示例,请求输入和输出:输入可能如下所示:

statistics = {
'length' : 200,
'minimum_value' : 5, 
'maximum_vlaue': 132, 
'mean': 30, 
'median' : 22,
'Q1': 13, 
'Q3': 68
}

所需的输出将如下所示:

similar_list = function_to_create_similar_list(statistics)
len(similar_list) # should be roughly 200
min(similar_list) # should be roughly 5
max(similar_list) # should be roughly 132
np.mean(similar_list) # should be roughly 30
np.median(similar_list) # should be roughly 22
np.quantile(similar_list, 0.25) # should be roughly 13
np.quantile(similar_list, 0.75) # should be roughly 68

function_to_create_similar_list是我要定义的函数
编辑2.
我的第一次编辑是不够的,我很抱歉。这里是我目前的代码:

def get_statistics(input_list): 
    output = {}
    output['length'] = len(input_list) 
    output['minimum_value'] = min(input_list) 
    output['maximum_value'] = max(input_list) 
    output['mean'] = np.mean(input_list) 
    output['median'] = np.median(input_list) 
    output['q1'] = np.quantile(input_list, 0.25)
    output['q3'] = np.quantile(input_list, 0.75)
    return output

def recreate_similar_list(statistics, maximum_deviation = 0.1 ): 
    sufficient_list_was_found = False
    while True: 
        candidate_list = [random.uniform(statistics['minimum_value'],statistics['maximum_value']) for _ in range(statistics['length'])]
        candidate_statistics = get_statistics(candidate_list)
        sufficient_list_was_found = True
        for key in statistics.keys(): 
            if np.abs(statistics[key] - candidate_statistics[key]) / statistics[key] > maximum_deviation:
                sufficient_list_was_found = False
                break
        if(sufficient_list_was_found): 
            return candidate_list

example_input_list_1 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,10]
recreated_list_1 = recreate_similar_list(get_statistics(example_input_list_1),0.3)
print(recreated_list_1)
print(get_statistics(recreated_list_1))

example_input_list_2 = [1,1,1,1,3,3,4,4,4,4,4,5,18,19,32,35,35,42,49,68]
recreated_list_2 = recreate_similar_list(get_statistics(example_input_list_2),0.3)
print(recreated_list_2)
print(get_statistics(recreated_list_2))

第一个例子可以找到一个解决方案。这对我来说并不奇怪。第二个例子没有(或者没有足够的时间)。这也不奇怪,因为recreate_similar_list函数生成的列表是均匀分布的。尽管这两个例子都代表了真实的的任务。(记住,我只得到了统计信息,而不是列表)
我希望这是一个足够的例子

svmlkihl

svmlkihl1#

您现有的解决方案很有趣,但实际上是一个bogo解决方案。有可能的直接解决方案不需要依赖于随机和检查。
比较简单的部分是创建一个正确长度的数组,并将所有五个最小值/最大值/四分位数放在适当的位置(这只适用于对问题的简单解释,并且有局限性)。
更棘手的部分是选择四分位数之间的“填充值”。这些填充值可以在一个四分位数区间内相同,因为唯一重要的是总和和边界。一个相当直接的方法是linear programming,通过Scipy的scipy.optimize.linprog。它通常用于有界线性代数问题,这是一个。对于参数,我们用途:

  • c的零点,最小化系数,因为我们不关心最小化
  • 对于等式约束矩阵A_eq,我们传递一个元素计数矩阵。这是一个长度为4的矩阵,因为有四个四分位数部分,每个部分的元素计数可能略有不同。在您的示例中,每个部分的元素计数都接近50。
  • 对于等式约束右侧向量B_eq,我们基于所需均值计算所有四分位数区间的所需总和。
  • 对于bounds,我们通过每个四分位数区间的边界。

一个棘手的方面是,这假设了容易划分的部分,以及使用lower方法的分位数计算。但是有at least thirteen methods!有些比其他更难用算法定位。而且,lower引入了统计偏差。我把解决这些边缘情况作为练习留给读者。但是这个例子很有效:

import numpy as np
from scipy.optimize import linprog

def solve(length: int, mean: float,
          minimum_value: float, q1: float, median: float, q3: float,
          maximum_value: float) -> np.ndarray:
    sections = (np.arange(5)*(length - 1))//4
    sizes = np.diff(sections) - 1
    quartiles = np.array((minimum_value, q1, median, q3, maximum_value))

    # (quartiles + sizes@x)/length = mean
    # sizes@x = mean*length - quartiles
    result = linprog(c=np.zeros_like(sizes),
                     A_eq=sizes[np.newaxis, :],
                     b_eq=np.array((mean*length - quartiles.sum(),)),
                     bounds=np.stack((quartiles[:-1], quartiles[1:]), axis=1),
                     method='highs')
    if not result.success:
        raise ValueError(result.message)

    x = np.empty(length)
    x[sections] = quartiles
    for i, inner in enumerate(result.x):
        i0, i1 = sections[i: i+2]
        x[i0+1: i1] = inner
    return x

def summarise(x: np.ndarray) -> dict[str, float]:
    q0, q1, q2, q3, q4 = np.quantile(
        a=x, q=np.linspace(0, 1, num=5), method='lower')
    return {'length': len(x), 'mean': x.mean(),
            'minimum_value': q0, 'q1': q1, 'median': q2, 'q3': q3, 'maximum_value': q4}

def test() -> None:
    statistics = {'length': 200, 'mean': 30,  # 27.7 - 58.7 are solvable
                  'minimum_value': 5, 'q1': 13, 'median': 22, 'q3': 68, 'maximum_value': 132}
    x = solve(**statistics)
    for k, v in summarise(x).items():
        assert np.isclose(v, statistics[k])

if __name__ == '__main__':
    test()

相关问题