Pandas DataFrame: количество одинаковых значений в разных столбцах в последовательности для каждой строки - PullRequest
0 голосов
/ 13 ноября 2018

Давайте предположим, что у меня есть pandas DataFrame в Python, который показывает имя лидера бизнес-единицы для разных юнитов с течением времени. Это может выглядеть примерно так

          Leader_Jan Leader_Feb Leader_Mar Leader_Apr
Unit1       Nina       Nina       Nina       Nina
Unit2       Lena       Lena        NaN       Lena
Unit3       Alex      Maria       Alex       Alex
Unit4     Emilia        NaN        NaN        NaN
Unit5        NaN    Corinna      Petra        NaN

и может быть воссоздан как:

import pandas as pd
import numpy as np
a = ['Nina','Nina','Nina','Nina']
b = ['Lena','Lena',np.NaN,'Lena']
c = ['Alex','Maria','Alex','Alex']
d = ['Emilia',np.NaN,np.NaN,np.NaN]
e = [np.NaN,'Corinna','Petra',np.NaN]
data = pd.DataFrame(data=[a,b,c,d,e], columns =['Leader_Jan','Leader_Feb','Leader_Mar','Leader_Apr'], index=['Unit1','Unit2','Unit3','Unit4','Unit5'])

Контекст: Я хотел бы выяснить, в каких руководителях подразделений остаются на очень короткие или очень длительные периоды (в месяцах), чтобы потом выяснить, есть ли командные конфликты в конкретных подразделениях моей компании.

Я хотел бы добавить к фрейму данных минимальные и максимальные значения (в месяцах) на единицу продолжительности пребывания лидеров за один непрерывный период. Из-за возможных прерываний (см. Блок 2 и Блок 3) я не могу просто использовать value_counts для разных имен в каждой строке. Мне скорее нужно найти длину последовательностей разных имен лидеров, разделенных значениями NaN и другими именами. Чтобы увидеть, что я считаю последовательностью, проверьте разные цвета на этом фото:

sequences_colored

Как вы можете видеть, прерывание, как в блоках 2 и 3, должно привести к нескольким периодам пребывания. Количество NaN-месяцев в последовательности не должно учитываться.

Результат должен выглядеть следующим образом:

      Leader_Jan Leader_Feb Leader_Mar Leader_Apr  Min_length_of_stay_leaders  \
Unit1       Nina       Nina       Nina       Nina                           4   
Unit2       Lena       Lena        NaN       Lena                           1   
Unit3       Alex      Maria       Alex       Alex                           1   
Unit4     Emilia        NaN        NaN        NaN                           1   
Unit5        NaN    Corinna      Petra        NaN                           1   

       Max_length_of_stay_leaders  
Unit1                           4  
Unit2                           2  
Unit3                           2  
Unit4                           1  
Unit5                           1 

Я знаю, что это может быть довольно сложным для подражания, но я бы любил любую помощь / советы и т. Д., Потому что я немного потерян здесь.

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Это на самом деле довольно просто, используя itertools.groupby :

from itertools import groupby

def min_max_durations(row):
    # the group object consumes the iterator, but we don't care about the values 
    # so we just sum "1" to get the length.
    # Taken from https://stackoverflow.com/questions/44490079/how-to-turn-an-itertools-grouper-object-into-a-list
    durations = [sum(1 for _ in group) for key, group in groupby(row) if not isinstance(key, float)]
    return min(durations), max(durations)

data["min_lengths_of_stay"], data["max_lengths_of_stay"] = zip(*data.apply(min_max_durations, axis=1))

Проверка экземпляра для float - это просто быстрый способ убрать значение NaN из подсчета здесь, вы можете сделать это сколь угодно сложным.

Это выводит правильные результаты (обратите внимание, что копия, вставляющая ваш код воспроизведения, имеет 3 записи "Alex" в Unit3, в отличие от вашего примера)

      Leader_Jan Leader_Feb Leader_Mar Leader_Apr  min_lengths_of_stay  \
Unit1       Nina       Nina       Nina       Nina                    4   
Unit2       Lena       Lena        NaN       Lena                    1   
Unit3      Maria       Alex       Alex       Alex                    1   
Unit4     Emilia        NaN        NaN        NaN                    1   
Unit5        NaN    Corinna      Petra        NaN                    1   
       max_lengths_of_stay  
Unit1                    4  
Unit2                    2  
Unit3                    3  
Unit4                    1  
Unit5                    1  
0 голосов
/ 13 ноября 2018

Это должно помочь вам начать -

temp = df.apply(lambda x: x.groupby((x != x.shift()).cumsum()).cumcount()+1, axis=1)

mins = temp.min(1)
maxs = temp.max(1)
mask = temp.apply(lambda x: x.is_monotonic_increasing and x.is_unique, axis=1)
mins.loc[mask] = maxs.loc[mask]
mins.name='Min_length_of_stay_leaders'
maxs.name='Max_length_of_stay_leaders'

df.join(mins).join(maxs)

Вывод

      Leader_Jan Leader_Feb Leader_Mar Leader_Apr  Min_length_of_stay_leaders  \
Unit1       Nina       Nina       Nina       Nina                           4   
Unit2       Lena       Lena        NaN       Lena                           1   
Unit3       Alex      Maria       Alex       Alex                           1   
Unit4     Emilia        NaN        NaN        NaN                           1   
Unit5        NaN    Corinna      Petra        NaN                           1   

       Max_length_of_stay_leaders  
Unit1                           4  
Unit2                           2  
Unit3                           2  
Unit4                           1  
Unit5                           1 

Объяснение

temp = df.apply(lambda x: x.groupby((x != x.shift()).cumsum()).cumcount()+1, axis=1)

Это дает вам последовательный подсчет лидеров, сгруппированных по их именам -

    Leader_Jan  Leader_Feb  Leader_Mar  Leader_Apr
Unit1   1   2   3   4
Unit2   1   2   1   1
Unit3   1   1   1   2
Unit4   1   1   1   1
Unit5   1   1   1   1

Просто извлеките max и min -

mins = temp.min(1)
maxs = temp.max(1)

Тогда возникает проблема Nina- она ​​оставалась на своем посту, поэтому в этом случае min тоже должно быть 4.

Так что только для этого граничного случая объект mask обнаруживает строго монотонно увеличивающиеся серии и заменяет на max, если это так.

Я все еще не уверен, будет ли онработать для всех случаев или нет, поэтому, пожалуйста, проверьте

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