Как рассчитать словари списков с помощью панд DataFrame? - PullRequest
0 голосов
/ 03 сентября 2018

У меня есть две строки в Python3.x, которые определены как имеющие одинаковую длину:

string1 = 'WGWFTSJKPGP'
string2 = 'DORKSRQKYJG'

Мне также дают целое число, которое должно представлять "начальный индекс" string2. В этом случае start_pos = 51.

Цель - создать словарь на основе индексов. Итак, string1 начинается с 0, string2 начинается с 51. Словарь "преобразования" этих координат выглядит следующим образом:

{0: 51, 1: 52, 2: 53, 3: 54, 4: 55, 5: 56, 6: 57, 7: 58, 8: 59, 9: 60, 10: 61}

, который можно построить (приведите переменные выше) с помощью:

convert_dict = {i: i + start_pos for i, _ in enumerate(string1)}

В настоящее время у меня есть эти данные в виде панды DataFrame:

import pandas as pd

dict1 = {'column1':['MXRBMVQDHF', 'LJNVTJOY', 'LJNVTJOY', 'LJNVTJOY', 'WHLAOECVQR'], 'column2':['DPBVNJYANX', 'UWRAWDOB', 'PEKUYUQR', 'WPMLFVFZ', 'CUTQVWHRIJ'], 'start':[79, 31, 52, 84, 18]}

df = pd.DataFrame(dict1)
print(df)
#       column1     column2  start
# 0  MXRBMVQDHF  DPBVNJYANX     79
# 1    LJNVTJOY    UWRAWDOB     31
# 2    LJNVTJOY    PEKUYUQR     52
# 3    LJNVTJOY    WPMLFVFZ     84
# 4  WHLAOECVQR  CUTQVWHRIJ     18

В столбце column1 несколько записей одной и той же строки. В этом случае словарь для координат с LJNVTJOY должен быть:

{0: [31, 52, 84], 1: [32, 53, 85], 2: [33, 54, 86], 3: [34, 55, 87], 
     4: [35, 56, 88], 5: [36, 57, 89], 6: [37, 58, 90], 7: [38, 59, 91]}

Я бы хотел взять этот DataFrame и вычислить аналогичные словари координат. Такое выражение .groupby('column1') выглядит так, как-то следует использовать .apply()? Я не уверен, как заполнить списки словаря, как это ...

Вот правильный вывод (с сохранением структуры DataFrame). Здесь DataFrame df2 имеет столбец 'new_column' такой, что он выглядит следующим образом:

df2.new_column
0    {0: 79, 1: 80, 2: 81, 3: 82, 4: 83, 5: 84, 6: ...
1    {0: [31, 52, 84], 1: [32, 53, 85], 2: [33, 54, 86], 3: [34, 55, 87], 4: [35, 56, 88], 5: [36, 57, 89], 6: [37, 58, 90], 7: [38, 59, 91]}
2    {0: 52, 1: 53, 2: 54, 3: 55, 4: 56, 5: 57, 6: ...
Name: new, dtype: object

Ответы [ 3 ]

0 голосов
/ 03 сентября 2018

Использование -

def dict_op(x):
    string1 = x['column1']
    string2 = x['column2']
    start_pos = x['start']
    x['val'] = {i: i + start_pos for i, _ in enumerate(string1)}
    return x

def zip_dict(x):
    b=pd.DataFrame(x)
    return {i:b.loc[:,i].tolist() for i in b.columns }

op = df.apply(dict_op, axis=1).groupby('column1')['val'].apply(list).apply(zip_dict)
print(op)

выход

column1
LJNVTJOY      {0: [31, 52, 84], 1: [32, 53, 85], 2: [33, 54,...
MXRBMVQDHF    {0: [79], 1: [80], 2: [81], 3: [82], 4: [83], ...
WHLAOECVQR    {0: [18], 1: [19], 2: [20], 3: [21], 4: [22], ...
Name: val, dtype: object

Объяснение

dict_op повторно использует ваш код, чтобы создать диктовку для каждой строки, а затем .apply(list) объединяет их вместе, образуя список диктов.

Затем zip_dict() создает вывод dict из промежуточного вывода.

Последняя часть, которую я не включил, это та часть, где, если длина списка равна 1, вы можете включить только первый элемент, получая вывод от {0: [79], 1: [80], 2: [81], 3: [82], 4: [83], ... до {0: 79, 1: 80, 2: 81, 3: 82, 4: 83, ...

0 голосов
/ 03 сентября 2018

Вот немного другой подход, использующий lambda и два zips.

df2 = df.groupby('column1')['start'].agg([('s', list)]).reset_index()
df2['l'] = df.column1.str.len()

df2.apply(lambda x: dict(zip(range(x['l'] + 1), zip(*[range(s, s + x['l'] + 1) for s in x['s']]))), axis = 1)

Усеченный вывод этого можно увидеть здесь (обратите внимание, что он возвращает кортежи, а не списки):

0    {0: (31, 52, 84), 1: (32, 53, 85), 2: (33, 54,...
1    {0: (79,), 1: (80,), 2: (81,), 3: (82,), 4: (8...
2    {0: (18,), 1: (19,), 2: (20,), 3: (21,), 4: (2...

Сначала, чтобы сократить длину шага apply, создайте DataFrame со значениями column1 и соответствующими начальными позициями. Кроме того, добавьте столбец с длиной column1 (при условии, что выполняется утверждение равной длины).

После этого необходимо объединить диапазон буквенных индексов column1 (от 0 до len(column1), который служит ключами) и тот же диапазон, смещенный на значения (значения) start.

Со вторым zip дела идут немного рискованно, потому что [range(s, s + x['l'] + 1) for s in x['s']] возвращает что-то похожее на это (для 'LJNVTJOY'):

[[31, 32, 33, 34, 35, 36, 37, 38, 39],
 [52, 53, 54, 55, 56, 57, 58, 59, 60],
 [84, 85, 86, 87, 88, 89, 90, 91, 92]]

Когда мы действительно хотим сгруппировать элементы, выровненные по вертикали, мы используем оператор «splat» или « unpacking » для подачи этих списков в zip. После того, как мы объединили эти списки, у нас есть список ключей и список (кортежей) значений, которые могут быть zipped в dict.

0 голосов
/ 03 сентября 2018

Сначала примените функцию groupby, чтобы объединить столбец «start» в виде списка

df2 = df.groupby("column1")["start"].apply(list).reset_index()

Теперь вы можете написать функцию для создания нового словарного столбца

def create_dict(row):
    new_dict = {}
    for i, j in enumerate(row["column1"]):
        if len(row["start"]) == 1:
            new_dict[i] = row["start"][0]+i
        else:
            for k in row["start"]:
                if i in new_dict:
                    new_dict[i].append(k + i)
                else:
                    new_dict[i] = [k + i]
    return new_dict

Наконец, примените эту функцию ко всем строкам df2

df2["new_column"] = df2.apply(create_dict, axis = 1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...