Динамическое присвоение уникальных значений - Python - PullRequest
4 голосов
/ 17 октября 2019

Я пытаюсь присвоить уникальные значения определенному распределению или группе. Хитрость заключается в том, что эти уникальные значения динамически начинаются и заканчиваются. Таким образом, группы будут содержать ранее увиденные значения и принимать новые уникальные значения в разные периоды времени. Что касается df, уникальные значения расположены в Place, а группы для выбора находятся в Available Group для каждого периода времени Period.

Вот общие принципы, которых я пытаюсь придерживаться:

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

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

3) После того, как Places назначен на Group, удерживайте до завершения Group. Если Group не станет NA или неравномерное распределение собраний

Чтобы понять, сколько Places происходит в настоящее время, я включил Total, который основан на том, если Placeзначение появляется снова. Я встречаю мои первые два руководства и частично третье. Когда Place назначено для Group, оно удерживается на том же Group до тех пор, пока Place не будет завершено (больше не появляется).

Однако я не ссылаюсь на Available Group чтобы понять, если это Group доступно. Когда Group становится недоступным, я бы хотел переставить эти Places по другому Available Group. Используя df ниже, назначение мест работает хорошо, уникальные места увеличиваются. Но как только они начинают финишировать, и группа 2 становится недоступной, эти места не перераспределяются в группу 1. На данный момент происходит только 3 места.

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'],                           
    })

Попытка:

# 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

def assign_place_label(group):

    ''' Create a label column that calculates the amount of unique meetings 
        throughout the racing schedule '''

    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 'freed' the index is reused to new place appearing after that
def Assign_Meetings_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(up.loc[ind_freed, "Index"])
        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 = Assign_Meetings_group(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)

Out:

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

Предполагаемый выход:

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

1 Ответ

1 голос
/ 28 октября 2019

Вот мое решение для вопроса, пожалуйста, найдите детали в комментарии

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],  
    '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'],                           
    })

# 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

def assign_place_label(group):

    ''' Create a label column that calculates the amount of unique meetings 
        throughout the racing schedule '''

    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()+1
    return group

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


def calc_groups(uniquePlaces, df2):

    ## group need to be changed only when a group starts or finishes
    change_points = np.sort(uniquePlaces[["Start", "End"]].values.ravel()).reshape(-1,1)

    ## for each change points find boolean indxes for places (True if place is in use at that point)
    inds = (change_points>=uniquePlaces["Start"].values) & (change_points<uniquePlaces["End"].values)

    ## all available indexes for place
    all_ind = set(uniquePlaces.index.values+1)
    prev_ind = np.array([0]*len(all_ind))

    result = []
    for ind in inds:
        ## copy prev_ind where place exists
        new_ind = prev_ind * ind
        ## mark places with index greater than available places with -1
        new_ind[new_ind>sum(ind)] = -1
        ## mark existing places with index 0 with -1
        new_ind[(new_ind==0) & ind] = -1

        available_ind = all_ind - set(new_ind[new_ind>0])

        ## replace indxes marked by -1 with minimum values from available_ind
        for i in range(len(new_ind)):
            if new_ind[i]==-1:
                new_ind[i] = min(available_ind)
                available_ind.remove(new_ind[i])

        result.append(new_ind)
        prev_ind = new_ind

    result = np.r_[result]
    repeats = np.r_[change_points[1:] - change_points[:-1], [[0]]].ravel()

    ## place index calculated only for change points, now fill the gap between change points
    ## by repeating index in the gap
    result = np.repeat(result, repeats, axis=0)

    df2["group"] = (result[np.arange(len(result)), df2["Place Label"].values]-1)//3 + 1
    return df2


df2 = calc_groups(uniquePlaces, df2)
df2.drop_duplicates(subset=['Period','Place'])

Результат

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