Получить первую ненулевую запись для каждого столбца и записать соответствующее значение индекса

У меня есть фрейм данных, который выглядит примерно так:

IndexMonth   Cus1   Cus2  Cus3  Cus4 ........ Cusn
2019-01       0     111    0      0           333
2019-02       0     111    0     666           0
2019-03      500     0     333    55           0  
2019-04      600     0     333    111          0
2019-05      600    100    0      111          0

Я хочу получить первый ненулевой месяц для каждого столбца Cus, а также последний ненулевой месяц. Если у Cus есть перерыв, и он начинается снова, я хочу, чтобы новое начало также было добавлено в колонку начала. Итак, мой вывод должен выглядеть примерно так:

       StartMonth          EndMonth
Cus1   2019-03             2019-05
Cus2   2019-01,2019-05     2019-02,2019-05
Cus3   2019-03             2019-04
Cus4   2019-02             2019-05
..
Cusn   2019-01             2019-01

Не могли бы вы подтвердить, насколько эффективно я могу добиться этого.

Да, это 0. (не null или na)

asimo 19.11.2022 11:05
Скраппинг поиска Apple App Store с помощью Python
Скраппинг поиска Apple App Store с помощью Python
📌Примечание: В этой статье я покажу вам, как скрапировать поиск Apple App Store и получить точно такой же результат, как на Apple iMac, потому что...
Редкие достижения на Github ✨
Редкие достижения на Github ✨
Редкая коллекция доступна в профиле на GitHub ✨
Мутабельность и переработка объектов в Python
Мутабельность и переработка объектов в Python
Объекты являются основной конструкцией любого языка ООП, и каждый язык определяет свой собственный синтаксис для их создания, обновления и...
Другой маршрут в Flask Python
Другой маршрут в Flask Python
Flask - это фреймворк, который поддерживает веб-приложения. В этой статье я покажу, как мы можем использовать @app .route в flask, чтобы иметь другую...
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
Проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
Python PyPDF2 - запись метаданных PDF
Python PyPDF2 - запись метаданных PDF
Python скрипт, который будет записывать метаданные в PDF файл, для этого мы будем использовать PDF ридер из библиотеки PyPDF2 . PyPDF2 - это...
2
1
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сначала я использую функцию транспонирования, затем я получаю первое значение, не равное 0, с помощью idxmax. Я получаю последнее значение с last_valid_index

df2=df.T
df2['first']=df2.ne(0).idxmax(axis=1)
df2['last']=df2[df2.columns[0:5]].mask(df2==0).apply(pd.Series.last_valid_index, axis=1)
#You should set the numbers 0 and 5 according to the number of columns. Here I am only getting the month columns.

print(df2)
'''
IndexMonth  2019-01  2019-02  2019-03  2019-04  2019-05    first     last
Cus1              0        0      500      600      600  2019-03  2019-05
Cus2            111      111        0        0      100  2019-01  2019-05
Cus3              0        0      333      333        0  2019-03  2019-04
Cus4              0      666       55      111      111  2019-02  2019-05
Cusn            333        0        0        0        0  2019-01  2019-01
'''

Первый шаг в порядке. Для второй фазы я пробовал что-то вроде этого, работает с этими данными, но я не уверен, что это будет работать правильно при изменении количества месяцев:

df2['check']=False
for i in range (0,len(df2.index)):
    col_name=df2['first'][i] #which columns is the first ?
    if len(df2.iloc[i].loc[lambda x : x == df2[col_name][i]]) >= 2: #if there is more than one of the same value

        df2['check'][i]=True #return true and fill the first first value with 0. To be able to get the latter when using idxmax.

        df2[col_name][i]=0
df2['first_2']=df2.ne(0).idxmax(axis=1)
df2['is_combine']=(df2['check']==True) & (df2['last'] != df2['first_2'])
df2['StartMonth']=np.where(df2['is_combine']==True,(df2['first'] +', '+df2['last']),df2['first'])
df2['EndMonth']=np.where(df2['is_combine']==True,(df2['first_2'] +', '+df2['last']),df2['last'])
df2=df2[['StartMonth','EndMonth']]
print(df2)
'''
IndexMonth        StartMonth          EndMonth
Cus1                 2019-03           2019-05
Cus2        2019-01, 2019-05  2019-02, 2019-05
Cus3                 2019-03           2019-04
Cus4                 2019-02           2019-05
Cusn                 2019-01           2019-01
'''
Ответ принят как подходящий

Вы можете использовать маски, чтобы сохранить первую/последнюю дату для последовательности ненулевых значений, а затем агрегировать:

df2 = df.set_index('IndexMonth')
m = df2.ne(0)

start = (df2
    .where(m&~m.shift(fill_value=False))
    .stack()
    .reset_index('IndexMonth')
    .groupby(level=0)['IndexMonth']
    .agg(','.join)
    .rename('StartMonth')
 )

end = (df2
    .where(m&~m.shift(-1, fill_value=False))
    .stack()
    .reset_index('IndexMonth')
    .groupby(level=0)['IndexMonth']
    .agg(','.join)
    .rename('EndMonth')
 )

out = pd.concat([start, end], axis=1)

print(out)

Вывод:

           StartMonth         EndMonth
Cus1          2019-03          2019-05
Cus2  2019-01,2019-05  2019-02,2019-05
Cus3          2019-03          2019-04
Cus4          2019-02          2019-05
Cusn          2019-01          2019-01

Спасибо @mozway Не могли бы вы подтвердить, как это работает под капотом?

asimo 20.11.2022 08:35

@asimo конечно. Я конвертирую в логический массив (True, если он не равен нулю), затем сдвигаю вверх или вниз. Первое значение — это True, а shift — это False. И наоборот, последнее значение — True, а следующее — False. Мы используем это для определения дат, а затем агрегируем по каждому клиенту.

mozway 20.11.2022 08:44

Другие вопросы по теме