Сравнение различных методов для сокращения списка повторяющихся значений в python (поддержание порядка и количества повторений) - PullRequest
0 голосов
/ 07 января 2020

Мне потребовался сценарий Python для сокращения списка значений, и я не был уверен, что это лучший способ выполнить sh. Приведенные ниже функции представляют собой набор различных подходов, которые были рекомендованы мне, и связанные с ними среды выполнения. Для справки, мои исходные данные для этих эталонных тестов использовали 100 списков приблизительно по 500 000 записей в каждом

Спасибо всем, кто указал мне правильное направление!

Пример ввода:

values = [3, 4, 4, 5, 3, 2, 7, 8, 9, 9, 9, 9, 9, 4, 4, 2, 1, 1]

Желаемый результат:

col_counts = [1, 2, 1, 1, 1, 1, 1, 5, 2, 1, 2]
col_values = [3, 4, 5, 3, 2, 7, 8, 9, 4, 2, 1]
Output String = 1*3 2*4 1*5 1*3 1*2 1*7 1*8 5*9 2*4 1*2 2*1

Сводные данные:

  • Использование Pandas было очень по скорости аналогичен ручной итерации по списку
  • Использование itertools с (k, len (list (g))) на самом деле 2x быстрее, чем ( k, сумма (1 для i in g)
  • Использование itertools w / len было самым быстрым методом ( 5x быстрее, чем pandas или ручное l oop)
  • Самый быстрый метод также был самым компактным: для построения выходной строки потребовалось всего 2 строки (против 28 в моем оригинале)

Код для быстрого метода:

def condense_values_2liner(values):
    grouped_values = [(len(list(g)), k) for k,g in groupby(values)]
    output_str = " ".join("%s*%s" % tup for tup in grouped_values)
    return output_str

Полный код для других методов:

# BENCHMARKED PERFORMANCE (100 LISTS OF APPROX. 500,000 values
# > condense_values_loop = 21.7s
# > condense_values_pandas = 21.2s
# > condense_values_itertools_sum = 10.4s
# > condense_values_itertools_len = 5.1s
# > condense_values_2liner = 4.8s

# > condense_values_loop = 21.7s
def condense_values_loop(values):
    prev_value = None
    cnt = 0
    value_counts = []
    first = True
    for value in values:
        if first: # First value
            cnt = 1
            prev_value = value
            first = False
        else:
            if value != prev_value:
                value_counts.append([cnt,prev_value])
                cnt = 1
                prev_value = value
            else:
                cnt += 1

    # Write final line to array (check for no values)
    value_counts.append([cnt,value])

    # Build output strings
    output_pairs = []
    for value_count in value_counts:
        entry = str(value_count[0]) + "*" + str(value_count[1])
        output_pairs.append(entry)
    output_str = ' '.join(output_pairs)
    return output_str


# > condense_values_pandas = 21.2s
def condense_values_pandas(values):
    df = pd.DataFrame({'value': values})
    df2 = (
        df
        .groupby(df['value'].ne(df['value'].shift()).cumsum())
        .count()
        .reset_index(drop=True)
        .assign(x=df.loc[df['value'].ne(df['value'].shift()), 'value'].tolist())
    )
    df2.columns = ['counts', 'value']
    df2['output_str'] = df2['counts'].astype(str) + '*' + df2['value'].astype(str)
    col_counts = df2['counts'].tolist()
    col_values = df2['value'].tolist()
    output_string = ' '.join(df2['output_str'])
    return output_string


# condense_values_itertools_sum = 10.4s
def condense_values_itertools_sum(values):
    grouped_values = [(k, sum(1 for i in g)) for k,g in groupby(values)]
    paired_values = []
    for pair in grouped_values:
        paired_values.append(str(pair[1]) + "*" + str(pair[0]))
    output_str = ' '.join(paired_values)
    return output_str


# condense_values_itertools_len = 5.1s
def condense_values_itertools_len(values):
    grouped_values = [(k, len(list(g))) for k,g in groupby(values)]
    paired_values = []
    for pair in grouped_values:
        paired_values.append(str(pair[1]) + "*" + str(pair[0]))
    output_str = ' '.join(paired_values)
    return output_str

# > condense_values_2liner = 4.8s
# > Note: 'join()' call required switching k/g from functions above
def condense_values_2liner(values):
    grouped_values = [(len(list(g)), k) for k,g in groupby(values)]
    output_str = " ".join("%s*%s" % tup for tup in grouped_values)
    return output_str
...