Поддержание отношения при расщеплении данных в функции python

У меня есть некоторые данные, и я хочу разбить его на более мелкие группы, поддерживающие общий коэффициент. Я написал функцию, которая возьмет входной сигнал из двух массивов и вычислит отношение размера, а затем скажет мне, сколько групп я могу разбить на это (если все группы имеют одинаковый размер), вот функция:

def cross_validation_group(train_data, test_data):
    import numpy as np
    from calculator import factors
    test_length = len(test_data)
    train_length = len(train_data)
    total_length = test_length + train_length
    ratio = test_length/float(total_length)
    possibilities = factors(total_length)
    print possibilities
    print possibilities[len(possibilities)-1] * ratio
    super_count = 0
    for i in possibilities:
        if i < len(possibilities)/2:
            pass
        else: 
            attempt = float(i * ratio)
            if attempt.is_integer():
                print str(i) + " is an option for total size with " +  str(attempt) + " as test size and " + str(i - attempt) + " as train size! This is with " + str(total_length/i) + " folds."
            else:
                pass
    folds = int(raw_input("So how many folds would you like to use? If no possibilities were given that would be sufficient, type 0: "))
    if folds != 0:
        total_size = total_length/folds
        test_size = float(total_size * ratio)
        train_size = total_size - test_size
        columns = train_data[0]
        columns= len(columns)
        groups = np.empty((folds,(test_size + train_size),columns))
        i = 0
        a = 0
        b = 0
        for j in range (0,folds):
            test_size_new = test_size * (j + 1)
            train_size_new = train_size * j
            total_size_new = (train_size + test_size) * (j + 1)
            cut_off = total_size_new - train_size
            p = 0
            while i < total_size_new:
                if i < cut_off:
                    groups[j,p] = test_data[a]
                    a += 1
                else:
                    groups[j,p] = train_data[b]
                    b += 1
                i += 1
                p += 1
        return groups
    else:
        print "This method cannot be used because the ratio cannot be maintained with equal group sizes other than for the options you were givens"

Итак, мой вопрос заключается в том, как я могу сделать это так, чтобы третий вход в функцию представлял собой количество сгибов и менял функцию вокруг так, чтобы вместо того, чтобы выполнять итерацию, чтобы убедиться, что каждая группа имеет одинаковую сумму с правильное соотношение, оно будет иметь только соотношение, но варьируется в размерах?

Дополнение для @JamesHolderness

Итак, ваш метод почти идеален, но вот одна проблема:

с длиной 357 и 143 с 9 краями, это возвращаемый список:

[(39, 16), (39, 16), (39, 16), (39, 16), (39, 16), (39, 16), (39, 16), (39, 16), (39, 16)]

теперь, когда вы добавляете столбцы, вы получаете следующее: 351 144

351 отлично, потому что он меньше 357, но 144 не работает, потому что он больше 143! Причиной этого является то, что 357 и 143 являются массивами массивов, поэтому 144-я строка этого массива не существует...

Ответ 1

Вот алгоритм, который, как я думаю, может сработать для вас.

Вы берете test_length и train_length и делят их GCD, чтобы получить соотношение как простую долю. Вы принимаете числитель и знаменатель, и вы добавляете их вместе, и это фактор размера для ваших групп.

Например, если отношение равно 3: 2, размер каждой группы должен быть кратным 5.

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

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

Пример кода:

total_length = test_length + train_length          
divisor = gcd(test_length,train_length)
test_multiple = test_length/divisor
train_multiple = train_length/divisor
total_multiple = test_multiple + train_multiple 

# Adjust the ratio if there isn't enough data for the requested folds
if total_length/total_multiple < folds:
  total_multiple = total_length/folds
  test_multiple = int(round(float(test_length)*total_multiple/total_length))
  train_multiple = total_multiple - test_multiple

groups = []
for i in range(folds,0,-1):
  float_size = float(total_length)/i
  int_size = int(float_size/total_multiple)*total_multiple
  test_size = int_size*test_multiple/total_multiple
  train_size = int_size*train_multiple/total_multiple
  test_length -= test_size    # keep track of the test data used
  train_length -= train_size  # keep track of the train data used
  total_length -= int_size
  groups.append((test_size,train_size))

# If the test_length or train_length are negative, we need to adjust the groups
# to "give back" some of the data.
distribute_overrun(groups,test_length,0)
distribute_overrun(groups,train_length,1)

Это обновлено, чтобы отслеживать размер, используемый в каждой группе (тест и поезд), но не волнуйтесь, если мы используем слишком много изначально.

Затем в конце, если какой-либо перерасход (т.е. test_length или train_length стал отрицательным), мы распределяем его, перерастаем обратно в группы, уменьшая соответствующую сторону отношения в таком количестве элементов, сколько требуется довести переполнение до нуля.

Функция distribute_overrun приведена ниже.

def distribute_overrun(groups,overrun,part):
    i = 0
    while overrun < 0:
      group = list(groups[i])
      group[part] -= 1
      groups[i] = tuple(group)
      overrun += 1
      i += 1

В конце этого, группы будут списком кортежей, содержащих test_size и train_size для каждой группы.

Если это похоже на то, что вам нужно, но вам нужно, чтобы я расширился на примере кода, просто дайте мне знать.

Ответ 2

В другом вопросе автор хотел сделать аналогичную перекрестную проверку, как ваша. Пожалуйста, посмотрите на этот ответ. Разработав этот ответ на свою проблему, это будет выглядеть так:

import numpy as np
# in both train_data the first line is used for the cross-validation,
# and the other lines will follow, so you can add as many lines as you want
test_data = np.array([ 0.,  1.,  2.,  3.,  4.,  5.])
train_data  = np.array([[ 0.09,  1.9,  1.1,  1.5,  4.2,  3.1,  5.1],
                       [    3,    4,  3.1,   10,   20,    2,    3]])

def cross_validation_group( test_data, train_data):
    om1,om2 = np.meshgrid(test_data,train_data[0])
    dist = (om1-om2)**2
    indexes = np.argsort( dist, axis=0 )
    return train_data[:, indexes[0]]

print cross_validation_group( test_data, train_data )
# array([[  0.09,   1.1 ,   1.9 ,   3.1 ,   4.2 ,   5.1 ],
#        [     3 ,  3.1 ,     4 ,     2 ,    20 ,     3 ]])

У вас будет train_data, соответствующий интервалу, определенному в test_data.