Воссоздать список на основе статистики

Мне дана следующая статистика массива:

  • длина
  • Минимум
  • Максимум
  • Средний
  • медиана
  • Квартили

Я должен воссоздать список с более или менее такой же статистикой. Я знаю, что список, для которого рассчитывалась статистика, не имеет нормального распределения.

Моя первая идея состояла в том, чтобы просто переборщить, создав список случайных чисел в заданном диапазоне и надеясь, что одно из них подойдет. Преимущество этого метода в том, что он работает. В то время как недостатком, очевидно, является эффективность.

Поэтому я ищу более эффективный способ решить эту проблему. Надеюсь, что кто-то может помочь...

P.S. В настоящее время я использую только numpy, но я не ограничиваюсь этим.

Редактировать 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, распределены равномерно. Хотя оба примера представляют реальную задачу. (Имейте в виду, что я получаю только статистику, а не список)

Я надеюсь, что теперь это достаточный пример

Можете ли вы дать образец ввода-вывода?

ashish singh 10.01.2023 16:15

Был добавлен в исходный вопрос как редактирование

Mai65 10.01.2023 16:33

@Reinderien исправил тип с Q2 на Q3. И добавил более подробный пример, надеюсь, теперь все в порядке.

Mai65 11.01.2023 09:04
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Потяните за рычаг выброса энергососущих проектов
Потяните за рычаг выброса энергососущих проектов
На этой неделе моя команда отменила проект, над которым я работал. Неделя усилий пошла насмарку.
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
2
3
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваше существующее решение интересно, но по сути является bogo-решением. Возможны прямые решения, которые не должны полагаться на случайную проверку.

Самая простая часть — создать массив правильной длины и поместить все пять минимальных/максимальных/квартилей в соответствующие позиции (это работает только для несколько простой интерпретации проблемы и имеет ограничения).

Более сложная часть состоит в том, чтобы выбрать «значения заполнения» между квартилями. Эти значения заполнения могут быть идентичными в пределах одного межквартильного раздела, потому что единственное, что имеет значение, — это сумма и границы. Одним из довольно простых способов является линейное программирование через Scipy's scipy.optimize.linprog. Обычно он используется для задач ограниченной линейной алгебры, и это одна из них. Для параметров мы используем:

  • Нули для c, коэффициенты минимизации, потому что мы не заботимся о минимизации
  • Для A_eq, матрицы ограничения равенства, мы передаем матрицу количества элементов. Это матрица длины 4, потому что есть четыре межквартильные секции, каждая из которых потенциально может иметь немного разное количество элементов. В вашем примере каждый из них будет близок к 50.
  • Для B_eq, правого вектора ограничения равенства, мы вычисляем желаемую сумму всех межквартильных разделов на основе желаемого среднего.
  • Для bounds мы передаем границы каждого межквартильного сечения.

Один сложный аспект заключается в том, что это предполагает легко разделяемые разделы и расчет квантилей с использованием метода lower. Но есть как минимум тринадцать способов! Некоторым будет сложнее ориентироваться с помощью алгоритма, чем другим. Кроме того, 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()

Другие вопросы по теме