Инкремент, основанный на другом столбце в пандах - PullRequest
0 голосов
/ 29 июня 2018

У меня есть 2 столбца: группа и диапазон уровня. У каждой «группы» есть список фруктов, а у каждого «диапазона уровней» есть диапазон уровней, таких как «L1-L4».

Желаемый результат - столбцы «Фрукты» и «Уровень» на изображении ниже.

Таким образом, если диапазон был «L2-L3», столбец уровня сказал бы «2» для 1 строки, а затем «3» для следующей строки. Я также хотел бы добавить каждый элемент в списке «Группа» в столбец «Фрукты».

Буду признателен за любую помощь! Спасибо!

enter image description here

Вот работа, которую я проделал:

Я создал 2 вспомогательных столбца: «level_repeat» и «grouping_repeat», чтобы помочь мне продублировать необходимые строки.

df['level_repeat'] = df['Level'].replace(['L1-L6', 'L1-L2', 'L1-L3', 'L4-L6', 'L3-L6', 'L2-L6'], [6, 2, 3, 2, 3, 4])

df['grouping_repeat'] = df['Group'].str.len()

df_new = pd.DataFrame([df.ix[idx] for idx in df.index
                        for _ in range(df.ix[idx]['level_repeat'])]).reset_index(drop=True)

df_new = pd.DataFrame([df_new.ix[idx] for idx in df_new.index
                        for _ in range(df_new.ix[idx]['grouping_repeat'])]).reset_index(drop=True)           

В результате у меня будет 10 строк для примера выше, где в группе 2 элемента, а диапазон уровней охватывает 5 уровней (2 * 5 = 10). Однако мне по-прежнему нужна помощь по вставке данных в столбцы «Фрукты» и «Уровень».

Ответы [ 3 ]

0 голосов
/ 29 июня 2018

Вот один из способов, я бы сначала создал столбец 'level_list' из 'Range Range' с range числами, поэтому для 'L2-L5' список будет [2,3,4,5 ].

df['level_list'] = (df['Level Range'].str.split('-',expand=True)
                     .stack().str[-1].unstack()
                     .apply(lambda x: range(int(x[0]),int(x[1])+1),1))

Теперь, используя продукт из itertools и два столбца со списком (Group и level_list), вы можете создать новый фрейм данных:

from itertools import product
df_new = pd.DataFrame([ [ind, group, level_range, g, l] 
                        for ind, group, level_range, level_list 
                             in df[['Group','Level Range', 'level_list']].itertuples() 
                                   for l, g in product(level_list, group) ], 
                      columns = ['original_ind','Group', 'Level Range', 'Fruit','Level'])

с вводом типа

df = pd.DataFrame({'Group':[['Apple','Banana']], 'Level Range': ['L2-L5']})

результат для df_new:

   original_ind            Group Level Range   Fruit  Level
0             0  [Apple, Banana]       L2-L5   Apple      2
1             0  [Apple, Banana]       L2-L5  Banana      2
2             0  [Apple, Banana]       L2-L5   Apple      3
3             0  [Apple, Banana]       L2-L5  Banana      3
4             0  [Apple, Banana]       L2-L5   Apple      4
5             0  [Apple, Banana]       L2-L5  Banana      4
6             0  [Apple, Banana]       L2-L5   Apple      5
7             0  [Apple, Banana]       L2-L5  Banana      5

Обратите внимание: если вас не интересует исходный индекс, вы можете удалить столбец или не создавать его

0 голосов
/ 29 июня 2018

вам нужно извлечь диапазон из строки L1-L5 как [1,2,3,4,5] и создать кадр данных с произведением из этого списка и списка фруктов [Apple, Banana].

Для этой операции можно использовать itertools.product или pd.MultiIndex.from_product.

Здесь я использую последнюю и вспомогательную функцию для построения диапазона.

def get_level_range(x):
    a, b = x.replace('L', '').split('-')
    return range(int(a), int(b)+1)

dframes = []
for _, x in df.iterrows():
    dframes.append(
        pd.DataFrame(
            index=pd.MultiIndex.from_product(
                [get_level_range(x['Level Range']), x.Group, 
                 [tuple(x.Group)], [x['Level Range']]], 
                names=['Level', 'Fruit', 'Group', 'Level Range']
            )
        ).reset_index()
    )

pd.concat(dframes)

# produces output:

   Level   Fruit            Group Level Range
0      1   Apple  (Apple, Banana)       L1-L5
1      1  Banana  (Apple, Banana)       L1-L5
2      2   Apple  (Apple, Banana)       L1-L5
3      2  Banana  (Apple, Banana)       L1-L5
4      3   Apple  (Apple, Banana)       L1-L5
5      3  Banana  (Apple, Banana)       L1-L5
6      4   Apple  (Apple, Banana)       L1-L5
7      4  Banana  (Apple, Banana)       L1-L5
8      5   Apple  (Apple, Banana)       L1-L5
9      5  Banana  (Apple, Banana)       L1-L5

Предостережение 1 заключается в том, что Group необходимо преобразовать из list в tuple, поскольку список не может быть хешируемым и поэтому не может использоваться как элемент индекса. Но вы можете преобразовать его обратно в list позже, если хотите, например:

out.Group = out.Group.apply(list)
0 голосов
/ 29 июня 2018

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

res = []
for _, row in df.iterrows():
    group = row['Group']
    lv_range_str = row['Level Range']

    #change this line if the format of 'Level Range' changes
    lv_range = range(lv_range_str[1], lv_range_str[4] + 1)

    res += [
        {
            'Group': group,
            'Level Range': lv_range_str,
            'Fruit': fruit,
            'Level': level
        }
        for level in lv_range
        for fruit in group
    ]

res = pd.DataFrame(res)

Это будет работать, только если все строки в Level Range имеют формат L{i}-L{j}, в противном случае вам нужно будет изменить определение lv_range
Это может занять некоторое время, если ваш набор данных большой, хотя

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...