У меня есть фрейм данных, который выглядит так:
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
Спасибо!
Обновлено: я обновил свой пример, чтобы он больше соответствовал реальным данным.






в count01 изменение:
(x[0] == 1)) --> (x[0] == 0))
и в count10 изменить:
(x[0] == 0)) --> (x[0] == 1))
Это должно работать.
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 Позвольте мне перепроверить. Я скопирую код из stackoverflow и дам вам знать, что получится на выходе.
@gribna Вот код Python code pastebin.com/eT7bYPzv, и он генерирует этот вывод pastebin.com/a6EVSg0Q. Так вроде работает
Я запустил код здесь с лучшим примером: repl.it/repls/StandardExhaustedBrain
@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) и другую строку.
поэтому вызов transform использует числа 0,1, а НЕ строку «0», «1»
@gribna Я также обновил код в ответе.
Код дает правильные результаты для небольшого фрагмента данных с несколькими группами «идентификаторов», но если я копирую и вставляю его для использования в реальном фрейме данных, он дает неправильные числа. (Очень большое количество.) Может ли это работать по-другому в фрейме данных, импортированном в формате csv?
@gribna Я не думаю, что есть причина, по которой он будет работать по-другому в df на основе csv. Вы можете поделиться CSV для проверки.
Вы можете сначала создать столбец с переходами внутри одного и того же идентификатора, а затем использовать 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 Я забыл первую строку при копировании из своих тестов ... Отредактировано.
Я попробовал это на своих реальных данных, и это дало неправильный результат. Например, эта последовательность (0 1 1 0 0 0 0 1 0) для одного идентификатора привела к count01 = 1 и count10 = 1.
@gribna: не могу воспроизвести эту проблему. Я получаю 2 за оба. Можете ли вы поделиться некоторыми данными, демонстрирующими ошибку?
Я обновил пример с реальными данными. Когда я запускаю ваш код, он дает мне id count0.01 count1.00 0 110000078451 3 3
@gribna: я мог бы воспроизвести и исправить. Я просто забыл убедиться, что limit был целым числом в смещенном фрейме данных. Отредактировано.
Ваш код работает с примером. Но применительно к реальным данным счет сбивается. Я проверил это на трех группах (импортированных из словаря) с короткими последовательностями, и это сработало. Тем не менее, он не работает с фреймом данных, загруженным из csv. Те же имена столбцов, все то же самое, и полученные числа отключены. Я не понимаю.
Не могли бы вы поделиться CSV во внешней папке (google, ...)?
Согласно предложенному вами изменению, тогда будут подсчитываться переходы с 1 на 0 и с 0 на 1 соответственно. Но какая-то другая ошибка в исходном коде все еще остается, так как результаты не всегда правильные.