Определите уникальный идентификатор для комбинаций Python pandas

#python #pandas

#python #pandas

Вопрос:

У меня есть данные, которые выглядят как приведенная ниже таблица.

 grp      dpt    cls  lvl
Grp1                  0
Grp1    dpt1          1
Grp1    dpt2          1 
Grp1    dpt1    cs1   2
Grp1    dpt1    cs2   2
Grp1    dpt2    cs1   2
Grp1    dpt2    cs2   2
Grp1    dpt2    cs3   2
 

У меня есть комбинации grp, dpt, cls, и уровни были назначены на основе позиции.
Этот список может меняться в зависимости от выбора пользователя из интерфейса.

что я хочу сделать, так это присвоить уникальный идентификатор для комбинаций, подобных приведенной ниже таблице.(SNO)

 grp      dpt     cls  lvl   SNO
Grp1                   0    0
Grp1    dpt1           1    1
Grp1    dpt2           1    2
Grp1    dpt1    cs1    2    11
Grp1    dpt1    cs2    2    12
Grp1    dpt2    cs1    2    21
Grp1    dpt2    cs2    2    22
Grp1    dpt2    cs3    2    23
 

Здесь у меня есть только один grp, поэтому SNO равен 0.

2 уникальных dpt, поэтому SNO равны 1 и 2 соответственно.

в зависимости от dpt1 соответствующие cls будут иметь SNO 11, 12 для dpt 1.

в зависимости от dpt2 соответствующие cls будут иметь SON 21, 22 для dpt 2.

 IF USER SELECT ONE MORE HIERARCHY AFTER CLS THEN THE NEXT LEVEL OF HIERARCHY WILL HAVE SNO's DEPENDING ON THE CLS VALUES. EG (111, 112, 113  or 121, 122, 123)
 

Пользователь может выбрать GRP, DPT, CLS или любую другую комбинацию также из интерфейса, чтобы данные соответственно изменились.

Все, что я хочу, это сделать процесс динамическим для назначения SNO.

Комментарии:

1. Я не уверен, что вы подразумеваете под нужным столбцом SNO, но если вы хотите, чтобы число в конце было значением категории, вы можете использовать df.str.split его для разделения, затем соединить три столбца вместе и сделать его целым числом. Результатом будет 100,110,120,111,112,121,…

Ответ №1:

 lists=['GRP','DPT','CLS']


grpNM = pd.DataFrame({lists[0]: df[lists[0]].unique()})

grpNM['SN_lvl'] = grpNM.index 1

df = df.merge(grpNM, how='left', on=lists[0])

df['SN_lvl'] = df['SN_lvl'].fillna(0)

df['SN_lvl'] = df['SN_lvl'].astype(int)

df['SNO'] = df['SN_lvl'].astype(str)

df.drop(['SN_lvl'], inplace=True, axis=1)

#FOR FIRST LEVEL ID ASSIGNMENT DONE MANUALLY 

#1st LEVEL OF ID ASSIGNMENT DONE MANUALLY WITHOUT RUUNING THE LOOP


#NEXT LEVELS ID CREATION HAS BEEN DONE INSIDE LOOP

sn_list = df['SNO'].unique()

tempF = pd.DataFrame()

l1=lists.copy()
l1=l1[1:]
print(l1)

for L in range(len(l1)):
    for i in sn_list:
    
        tempDF = pd.DataFrame({l1[L]: df[df['SNO'] == i][l1[L]].unique()[1:]})
    
        tempDF['SN_lvl'] = tempDF.index 1
        
        tempDF['SN_lvl'].astype(int)
        
        tempDF['SNO'] = i
    
        tempF = tempF.append(tempDF, ignore_index=False) 
        
    df = df.merge(tempF, how='left', on=['SNO',l1[L]])
    
    df['SN_lvl'] = df['SN_lvl'].astype('O')    
    df.loc[df['SN_lvl'].notnull(), 'SN_lvl'] = df.loc[df['SN_lvl'].notnull(), 'SN_lvl'].astype(int)
    
    df['SN_lvl'] = df['SN_lvl'].fillna('')
    
    df['SNO'] = df['SNO']   df['SN_lvl'].astype(str)
    
    df.drop(['SN_lvl'], inplace=True, axis=1)  
    
    sn_list = df['SNO'].unique()[1:]  
 

Использовал этот подход, чтобы достичь цели.
Использовал список, поскольку я хотел сделать его динамическим на основе списка, переданного пользователем.

Ответ №2:

 # create a map for every column's content to unique number
col_map = dict()
col_map['grp'] = {'Grp1':'0'}
for col in ['dpt', 'cls']:
    cond = df[col] != ''
    obj = df.loc[cond, col].sort_values().dropna().unique()
    col_map[col] = dict(zip(obj, map(str,range(1, len(obj) 1))))

col_map
# {'grp': {'Grp1': '0'},
#  'dpt': {'dpt1': '1', 'dpt2': '2'},
#  'cls': {'cs1': '1', 'cs2': '2', 'cs3': '3'}}

# map with col_map content to number
for col in col_map:
    df[col '_m'] = df[col].map(col_map[col])
df = df.fillna('')

df['tag'] = df[['grp_m','dpt_m','cls_m']].apply(lambda x: ''.join(x), axis=1).map(list)
df['lvl'] = df['lvl'].astype(int)
df['SNO'] = df.apply(lambda x: ''.join(x.tag[-x.lvl:]), axis=1)

print(df)

#     grp   dpt  cls  lvl grp_m dpt_m cls_m        tag SNO
# 0  Grp1               0     0                    [0]   0
# 1  Grp1  dpt1         1     0     1           [0, 1]   1
# 2  Grp1  dpt2         1     0     2           [0, 2]   2
# 3  Grp1  dpt1  cs1    2     0     1     1  [0, 1, 1]  11
# 4  Grp1  dpt1  cs2    2     0     1     2  [0, 1, 2]  12
# 5  Grp1  dpt2  cs1    2     0     2     1  [0, 2, 1]  21
# 6  Grp1  dpt2  cs2    2     0     2     2  [0, 2, 2]  22
# 7  Grp1  dpt2  cs3    2     0     2     3  [0, 2, 3]  23
 

Ответ №3:

SNO представляет собой комбинацию чисел в конце каждого из столбцов от столбца № 1 до предпоследнего столбца. Вы можете объединить все эти столбцы и извлечь числа, встречающиеся в конце, используя регулярное выражение. (В противном случае для обхода всех столбцов потребовался бы цикл). Объединение с ## , чтобы просто определить число, встречающееся в конце значения каждого из этих столбцов

 df['SNO'] = (df.iloc[:, 1:-1] # this will take all the columns after grp and before lvl
             .apply(lambda x: "##".join(x.values.astype(str)) "##", axis=1)
             .str.extractall(r'(?P<SNO>d )##') # extract all the numbers before ##
             .groupby(level=0)['SNO'].apply(lambda x: "".join(list(x))))

     grp    dpt     cls  lvl   SNO
0   Grp1                 0     NaN
1   Grp1    dpt1         1     1
2   Grp1    dpt2         1     2
3   Grp1    dpt1    cs1  2     11
4   Grp1    dpt1    cs2  2     12
5   Grp1    dpt2    cs1  2     21
6   Grp1    dpt2    cs2  2     22
7   Grp1    dpt2    cs3  2     23
 

Для заполнения первой строки SNO потребуется group no - 1

 df['SNO'] = df['SNO'].fillna((df['grp'].str.extract(r'(d )

Промежуточные шаги выводят:

 df.iloc[:, 1:-1].apply(lambda x: "##".join(x.values.astype(str)) "##", axis=1)

0    ####       
1    dpt1####   
2    dpt2####   
3    dpt1##cs1##
4    dpt1##cs2##
5    dpt2##cs1##
6    dpt2##cs2##
7    dpt2##cs3##
dtype: object

(df.iloc[:, 1:-1].apply(lambda x: "##".join(x.values.astype(str)) "##", axis=1)
 .str.extractall(r'(?P<SNO>d )##'))

        SNO
  match 
1   0   1
2   0   2
3   0   1
    1   1
4   0   1
    1   2
5   0   2
    1   1
6   0   2
    1   2
7   0   2
    1   3
 


).astype(int)-1)[0])

grp dpt cls lvl SNO
0 Grp1 0 0
1 Grp1 dpt1 1 1
2 Grp1 dpt2 1 2
3 Grp1 dpt1 cs1 2 11
4 Grp1 dpt1 cs2 2 12
5 Grp1 dpt2 cs1 2 21
6 Grp1 dpt2 cs2 2 22
7 Grp1 dpt2 cs3 2 23

Промежуточные шаги выводят: