Значения столбца времени подсчета изменяются

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

df = DataFrame({'date': {379724: '2017-01-31',
  379725: '2017-01-31',
  414510: '2017-02-14',
  414509: '2017-02-28',
  414511: '2017-02-28',
  507215: '2017-04-27',
  507213: '2017-04-27',
  507214: '2017-04-27',
  507235: '2017-04-27',
  562139: '2017-04-27',
  672967: '2017-07-27',
  672968: '2017-07-27',
  672969: '2017-07-27',
  910729: '2017-12-07',
  990263: '2018-01-30',
  990265: '2018-01-30',
  990264: '2018-01-30',
  121543: '2018-06-26',
  255129: '2018-09-20'},
 'id': {379724: '110000078451',
  379725: '110000078451',
  414510: '110000078451',
  414509: '110000078451',
  414511: '110000078451',
  507215: '110000078451',
  507213: '110000078451',
  507214: '110000078451',
  507235: '110000078451',
  562139: '110000078451',
  672967: '110000078451',
  672968: '110000078451',
  672969: '110000078451',
  910729: '110000078451',
  990263: '110000078451',
  990265: '110000078451',
  990264: '110000078451',
  121543: '110000078451',
  255129: '110000078451'},
 'limit': {379724: 0,
  379725: 1,
  414510: 1,
  414509: 0,
  414511: 0,
  507215: 0,
  507213: 0,
  507214: 1,
  507235: 0,
  562139: 0,
  672967: 0,
  672968: 0,
  672969: 0,
  910729: 0,
  990263: 0,
  990265: 0,
  990264: 0,
  121543: 0,
  255129: 0})

И мне нужно подсчитать, сколько раз значение в 'limit' меняется на другое для каждой группы 'id'.

Код, который я придумал:

count01 = df.groupby('id')['limit'].rolling(2,min_periods=1)
.apply(lambda x: ((x[0] != x[-1]) & (x[0] == 1)), raw=True)
.groupby('id').sum().astype(int).reset_index(name='count01')

count10 = df.groupby('id')['limit'].rolling(2,min_periods=1)
.apply(lambda x: ((x[0] != x[-1]) & (x[0] == 0)), raw=True)
.groupby('id').sum().astype(int).reset_index(name='count10')

count_total = count01.merge(count10, on='id')

Иногда он дает правильные результаты, а иногда нет. Я думаю, что первому значению apply в группе может быть присвоено значение NaN, и это влияет на результат, но, возможно, это не так.

Результат должен быть:

id          | count01 | count10
-------------------------------
110000078451| 2       | 2

Спасибо!

Обновлено: я обновил свой пример, чтобы он больше соответствовал реальным данным.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
54
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

в count01 изменение:

(x[0] == 1)) --> (x[0] == 0))

и в count10 изменить:

(x[0] == 0)) --> (x[0] == 1))

Согласно предложенному вами изменению, тогда будут подсчитываться переходы с 1 на 0 и с 0 на 1 соответственно. Но какая-то другая ошибка в исходном коде все еще остается, так как результаты не всегда правильные.

gribna 04.07.2019 14:03

Это должно работать.

import pandas as pd


def limit_change_counter(limits, _from, _to):
    tmp = list(limits)
    counter = 0
    for idx, limit in enumerate(tmp):
        if idx > 0:
            if tmp[idx - 1] == _from and limit == _to:
                counter += 1
    return counter


df = pd.DataFrame.from_dict({'date': {379724: '2017-01-31',
                                      379725: '2017-01-31',
                                      414510: '2017-02-14',
                                      414509: '2017-02-28',
                                      414511: '2017-02-28',
                                      507215: '2017-04-27',
                                      507213: '2017-04-27',
                                      507214: '2017-04-27',
                                      507235: '2017-04-27',
                                      562139: '2017-04-27',
                                      672967: '2017-07-27',
                                      672968: '2017-07-27',
                                      672969: '2017-07-27',
                                      910729: '2017-12-07',
                                      990263: '2018-01-30',
                                      990265: '2018-01-30',
                                      990264: '2018-01-30',
                                      121543: '2018-06-26',
                                      255129: '2018-09-20'},
                             'id': {379724: '110000078451',
                                    379725: '110000078451',
                                    414510: '110000078451',
                                    414509: '110000078451',
                                    414511: '110000078451',
                                    507215: '110000078451',
                                    507213: '110000078451',
                                    507214: '110000078451',
                                    507235: '110000078451',
                                    562139: '110000078451',
                                    672967: '110000078451',
                                    672968: '110000078451',
                                    672969: '110000078451',
                                    910729: '110000078451',
                                    990263: '110000078451',
                                    990265: '110000078451',
                                    990264: '110000078451',
                                    121543: '110000078451',
                                    255129: '110000078451'},
                             'limit': {379724: 0,
                                       379725: 1,
                                       414510: 1,
                                       414509: 0,
                                       414511: 0,
                                       507215: 0,
                                       507213: 0,
                                       507214: 1,
                                       507235: 0,
                                       562139: 0,
                                       672967: 0,
                                       672968: 0,
                                       672969: 0,
                                       910729: 0,
                                       990263: 0,
                                       990265: 0,
                                       990264: 0,
                                       121543: 0,
                                       255129: 0}})

df.sort_values(by='date', inplace=True)
print(df)

df['limit_changes_0_to_1'] = df.groupby(['id'])['limit'].transform(limit_change_counter, 0, 1)
df['limit_changes_1_to_0'] = df.groupby(['id'])['limit'].transform(limit_change_counter, 1, 0)
df.drop_duplicates(subset = "id", keep = "first", inplace=True)

print(df)

Цель состоит в том, чтобы подсчитать изменения лимита в каждой группе идентификаторов для каждого уникального значения лимита (0 и 1). Таким образом, есть изменение 0->1 и изменение 1->0, которые нужно учитывать отдельно.

gribna 04.07.2019 14:01

@грибна Понял. Код был изменен.

balderman 04.07.2019 14:09

Я скопировал ваш код, но он всегда дает мне нули в результатах.

gribna 04.07.2019 15:55

@gribna Позвольте мне перепроверить. Я скопирую код из stackoverflow и дам вам знать, что получится на выходе.

balderman 04.07.2019 15:56

@gribna Вот код Python code pastebin.com/eT7bYPzv, и он генерирует этот вывод pastebin.com/a6EVSg0Q. Так вроде работает

balderman 04.07.2019 16:00

Я запустил код здесь с лучшим примером: repl.it/repls/StandardExhaustedBrain

gribna 04.07.2019 16:20

@gribna Понятно :-). Просто измените df['limit_changes_0_to_1'] = df.groupby(['id'])['limit'].transform(limit_change_counter, '0', '1') на df['limit_changes_0_to_1'] = df.groupby(['id'])['limit'].transform(limit_change_counter, 0, 1) и другую строку.

balderman 04.07.2019 16:26

поэтому вызов transform использует числа 0,1, а НЕ строку «0», «1»

balderman 04.07.2019 16:29

@gribna Я также обновил код в ответе.

balderman 05.07.2019 09:10

Код дает правильные результаты для небольшого фрагмента данных с несколькими группами «идентификаторов», но если я копирую и вставляю его для использования в реальном фрейме данных, он дает неправильные числа. (Очень большое количество.) Может ли это работать по-другому в фрейме данных, импортированном в формате csv?

gribna 05.07.2019 11:24

@gribna Я не думаю, что есть причина, по которой он будет работать по-другому в df на основе csv. Вы можете поделиться CSV для проверки.

balderman 05.07.2019 12:01
Ответ принят как подходящий

Вы можете сначала создать столбец с переходами внутри одного и того же идентификатора, а затем использовать pivot_table для подсчета этих переходов:

df2 = df.shift()
df2['limit'] = df2['limit'].bfill().astype(int)  # force limit to type int in shifted df
df.loc[(df.id==df2.id)&(df.limit!=df2.limit),'transition'] = \
                                   df2.limit.astype(str)+df.limit.astype(str)

resul = df.pivot_table(index='id', columns='transition', aggfunc='count',values='date', fill_value=0)

давая:

transition  01  10
id                
111          2   1
22           0   1

Вы можете улучшить презентацию:

resul = resul.rename(columns=lambda x: 'count'+x).rename_axis('', axis=1).reset_index()

чтобы наконец получить:

    id  count01  count10
0  111        2        1
1   22        0        1

@gribna Я забыл первую строку при копировании из своих тестов ... Отредактировано.

Serge Ballesta 04.07.2019 14:01

Я попробовал это на своих реальных данных, и это дало неправильный результат. Например, эта последовательность (0 1 1 0 0 0 0 1 0) для одного идентификатора привела к count01 = 1 и count10 = 1.

gribna 04.07.2019 14:37

@gribna: не могу воспроизвести эту проблему. Я получаю 2 за оба. Можете ли вы поделиться некоторыми данными, демонстрирующими ошибку?

Serge Ballesta 04.07.2019 14:54

Я обновил пример с реальными данными. Когда я запускаю ваш код, он дает мне id count0.01 count1.00 0 110000078451 3 3

gribna 04.07.2019 15:50

@gribna: я мог бы воспроизвести и исправить. Я просто забыл убедиться, что limit был целым числом в смещенном фрейме данных. Отредактировано.

Serge Ballesta 04.07.2019 16:08

Ваш код работает с примером. Но применительно к реальным данным счет сбивается. Я проверил это на трех группах (импортированных из словаря) с короткими последовательностями, и это сработало. Тем не менее, он не работает с фреймом данных, загруженным из csv. Те же имена столбцов, все то же самое, и полученные числа отключены. Я не понимаю.

gribna 05.07.2019 11:22

Не могли бы вы поделиться CSV во внешней папке (google, ...)?

Serge Ballesta 05.07.2019 13:10

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