Выделите значения из разных вариантов - панды - PullRequest
1 голос
/ 20 сентября 2019

У меня есть df, который содержит многочисленные Places в повторяющиеся периоды времени.Эти Places начинаются и заканчиваются случайным образом.Для каждого периода времени я хочу присвоить каждому уникальному месту значение Group.Основные правила при этом:

1) Каждый Group может содержать не более 3 уникальных Places одновременно

2) Уникальный Places должен быть равномерно распределен по каждому Group

Я взял очень маленький подраздел df.Есть 7 уникальных значений (но не более 5 встречающихся одновременно) и 2 Groups на выбор.Но на практике df может содержать до 50 уникальных значений, которые заканчиваются и заканчиваются, а также различные периоды времени, которые будут распределены по максимуму в 6 групп.

Чтобы понять, сколько Placesв настоящее время я включил Total, который основан на том, если Place появится снова.

df содержит все доступные Groups для каждого уникального Place для каждого Period,Места Golf и Club будут закончены, но мы предполагаем, что все остальные места продолжаются, как они появляются позже в df.

df = pd.DataFrame({
    'Period' : [1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,5,6,6],  
    'Place' : ['CLUB','CLUB','CLUB','HOME','HOME','AWAY','AWAY','WORK','WORK','AWAY','AWAY','GOLF','GOLF','CLUB','CLUB','POOL','POOL','HOME','HOME','WORK','WORK','AWAY','AWAY','POOL','POOL','TENNIS','TENNIS'],                                
    'Total' : [1,1,1,2,2,3,3,4,4,4,4,5,5,4,4,4,4,4,4,4,4,4,4,4,4,5,5],                            
    'Available Group' : ['1','2','1','2','1','2','1','2','1','1','2','1','2','2','1','2','1','2','1','2','1','1','2','1','2','2','1'],                           
    })

Основная проблема, которая вызывает у меня проблемы, - Places появляются / существуют динамически.На этом они заканчиваются, а новые начинаются случайным образом.Таким образом, назначение и распределение текущего уникального Places должно учитывать эту концепцию

Попытка:

def AssignPlace(df):
        uniquePlaces = df['Place'].unique()
        G3 = dict(zip(uniquePlaces, np.arange(len(uniquePlaces)) // 3 + 1))
        df['Assigned Group'] = df['Place'].map(G3)
        return df

df = df.groupby('Available Group', sort=False).apply(AssignPlace)
df = df.drop_duplicates(subset = ['Period','Place'])

Out:

    Period   Place  Total Available Group  Assigned Group
0   1       CLUB    1      1               1             
1   2       CLUB    1      2               1             
3   2       HOME    2      2               1             
5   2       AWAY    3      2               1             
7   3       WORK    4      2               2             
9   3       AWAY    4      1               1             
11  3       GOLF    5      1               2  #GOLF FINISHES SO 4 OCCURING FROM NEXT ROW            
13  4       CLUB    4      2               1  #CLUB FINISHES BUT POOL STARTS SO STILL 4 OCCURING FROM NEXT ROW           
15  4       POOL    4      2               2             
17  4       HOME    4      2               1             
19  5       WORK    4      2               2             
21  5       AWAY    4      1               1             
23  5       POOL    4      1               2             
25  6       TENNIS  5      2               3  #Signifies issue

Последняя строка отображает начало проблемы.Назначенная группа правильно измеряет это место как седьмое уникальное значение, но она не учитывает текущих уникальных значений.По завершении Club и Golf они представляют собой только 5 текущих значений и 2 доступных группы.Но он возвращается Group 3.Поэтому каждое новое уникальное значение будет продолжать учитываться, а не учитывать текущие уникальные значения.

Предполагаемый вывод, TENNIS Назначенная группа теперь 1 вместо 3:

    Period   Place  Total Available Group  Assigned Group
0   1       CLUB    1      1               1             
1   2       CLUB    1      2               1             
3   2       HOME    2      2               1             
5   2       AWAY    3      2               1             
7   3       WORK    4      2               2             
9   3       AWAY    4      1               1             
11  3       GOLF    5      1               2             
13  4       CLUB    4      2               1             
15  4       POOL    4      2               2             
17  4       HOME    4      2               1             
19  5       WORK    4      2               2             
21  5       AWAY    4      1               1             
23  5       POOL    4      1               2             
25  6       TENNIS  5      2               1 

1 Ответ

2 голосов
/ 25 сентября 2019

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

ПРИМЕЧАНИЕ : я добавил 5 пустых строк внизу, чтобы имитировать, что эти места будут появляться последними в df.Поэтому, пожалуйста, игнорируйте строки с периодом = 0

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'Period' : [1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,5,6,6,0,0,0,0,0],  
    'Place' : ['CLUB','CLUB','CLUB','HOME','HOME','AWAY','AWAY','WORK','WORK','AWAY','AWAY','GOLF','GOLF','CLUB','CLUB','POOL','POOL','HOME','HOME','WORK','WORK','AWAY','AWAY','POOL','POOL','TENNIS','TENNIS', "AWAY","HOME","POOL","WORK", "TENNIS"],                                
#     'Total' : [1,1,1,2,2,3,3,4,4,4,4,5,5,4,4,4,4,4,4,4,4,4,4,4,4,5,5,0,0,0,0,0],                            
#     'Available Group' : ['1','2','1','2','1','2','1','2','1','1','2','1','2','2','1','2','1','2','1','2','1','1','2','1','2','2','1',0,0,0,0,0],                           
    })

# df to store all unique places
uniquePlaces = pd.DataFrame(df["Place"].unique(), columns=["Place"])
# Start stores index of df where the place appears 1st
uniquePlaces["Start"] = -1
# End stores index of df where the place appears last 
uniquePlaces["End"] = -1

## adds new column "Place Label" which is label encoded value for a place
## "Place Label" may not be necessary but it may improve performance when looking up and merging
## this function also updates Start and End of current label in group
def assign_place_label(group):
    label=uniquePlaces[uniquePlaces["Place"]==group.name].index[0]
    group["Place Label"] = label
    uniquePlaces.loc[label, "Start"] = group.index.min()
    uniquePlaces.loc[label, "End"] = group.index.max()
    return group

## based on Start and End of each place assign index to each place
## when a freed the index is reused to new place appearing after that
def get_dynamic_group(up):
    up["Index"] = 0
    up["Freed"] = False
    max_ind=0
    free_indx = []
    for i in range(len(up)):
        ind_freed = up.index[(up["End"]<up.iloc[i]["Start"]) & (~up["Freed"])]

        free = list(ind_freed.values)
        free_indx += free
        up.loc[ind_freed, "Freed"] = True

        if len(free_indx)>0:
            m = min(free_indx)
            up.loc[i, "Index"] = m
            free_indx.remove(m)

        else:
            up.loc[i, "Index"] = max_ind
            max_ind+=1


    up["Group"] = up["Index"]//3+1

    return up  

df2 = df.groupby("Place").apply(assign_place_label)
uniquePlaces = get_dynamic_group(uniquePlaces)

display(uniquePlaces)

df3 = df2[df2.Period!=0].drop_duplicates(subset = ['Period','Place'])
result = df3.merge(uniquePlaces[["Group"]], how="left", left_on="Place Label", right_index=True, sort=False)
display(result)

Вывод

    Period  Place   Place Label Group
0   1   CLUB    0   1
1   2   CLUB    0   1
3   2   HOME    1   1
5   2   AWAY    2   1
7   3   WORK    3   2
9   3   AWAY    2   1
11  3   GOLF    4   2
13  4   CLUB    0   1
15  4   POOL    5   2
17  4   HOME    1   1
19  5   WORK    3   2
21  5   AWAY    2   1
23  5   POOL    5   2
25  6   TENNIS  6   1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...