Подсчитывать последовательные одинаковые числа во фрейме данных

#python #python-3.x

#python #python-3.x

Вопрос:

Я пытаюсь подсчитать последовательные одинаковые значения в этом фрейме данных ниже:

Фрейм данных

DF:

 {'obligacion': {0: 200000000123,  1: 200000000123,  2: 200000000123,  3: 200000000123,  4: 00000000123,  5: 200000000123,  6: 200000000123,  7: 200000000123,  8: 200000000123,  9: 200000000123,  0: 200000000123,  11: 200000000123,  12: 200000000123,  13: 200000000123,  14: 200000000123,  15: 00000000123,  16: 200000000123,  17: 200000000123}, 0: {0: 'mora18',  1: 'mora17',  2: 'mora16',  3: mora15',  4: 'mora14',  5: 'mora13',  6: 'mora12',  7: 'mora11',  8: 'mora10',  9: 'mora9',  10: 'mora8',  1: 'mora7',  12: 'mora6',  13: 'mora5',  14: 'mora4',  15: 'mora3',  16: 'mora2',  17: 'mora1'}, dias_mora': {0: '-1',  1: '-1',  2: '-1',  3: '-1',  4: '-1',  5: '-1',  6: '-1',  7: '4',  8: '6',  9: 0',  10: '8',  11: '9',  12: '7',  13: '10',  14: '3',  15: '2',  16: '3',  17: '2'}}
  

Итак, я хочу, чтобы на выходе было что-то вроде этого:

[200000000123: (-1, 7),
(4, 1),
(6, 1),
(0, 1),
(8, 1),
(9, 1),
(7, 1),
(10, 1),
(3, 1),
(2, 1),
(3, 1),
(2, 1)]

Идентификационный номер, за которым следует последовательный подсчет чисел, пример:

Число -1 повторяется 7 раз подряд

—————ОБНОВИТЬ——————

ИСПОЛЬЗУЕМЫЙ КОД

 import pandas as pd 
data = {
 'obligacion': [200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123]  
               [200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444],
 '0': [ 'mora18', 'mora17', 'mora16', 'mora15', 'mora14', 'mora13', 'mora12', 'mora11', 'mora10', 'mora9', 'mora8', 'mora7', 'mora6', 'mora5', 'mora4', 'mora3', 'mora2', 'mora1']  
      [ 'kiwi18', 'kiwi17', 'kiwi16', 'kiwi15', 'kiwi14', 'kiwi13', 'kiwi12', 'kiwi11', 'kiwi10', 'kiwi9', 'kiwi8', 'kiwi7', 'kiwi6', 'kiwi5', 'kiwi4', 'kiwi3', 'kiwi2', 'kiwi1'], 
 'dias_mora': [ '-1', '-1', '-1', '-1', '-1', '-1', '-1', '4', '6', '0', '8', '9', '7', '10', '3', '2', '3', '2']  
              [ '12', '0', '4', '4', '4', '7', '10', '4', '-6', '-7', '8', '8', '17', '10', '10', '-2', '3', '2']
}

df = pd.DataFrame.from_dict(data)  # convert dictionary to dataframe

dict_count = {}
for nid in df.obligacion.unique():
    vector_mora = df['dias_mora'][df.obligacion == nid].values
    groups = groupby(vector_mora)
    result = [(label, sum(1 for _ in group)) for label, group in groups] 
    dict_count[nid] = result
dict_count
  

Ответ №1:

Я не смог найти умный способ сделать это только с помощью pandas, поэтому потребовались список и циклы.

 import pandas as pd

data = {
     'obligacion': [200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123]  
                   [200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444],
     '0': [ 'mora18', 'mora17', 'mora16', 'mora15', 'mora14', 'mora13', 'mora12', 'mora11', 'mora10', 'mora9', 'mora8', 'mora7', 'mora6', 'mora5', 'mora4', 'mora3', 'mora2', 'mora1']  
          [ 'kiwi18', 'kiwi17', 'kiwi16', 'kiwi15', 'kiwi14', 'kiwi13', 'kiwi12', 'kiwi11', 'kiwi10', 'kiwi9', 'kiwi8', 'kiwi7', 'kiwi6', 'kiwi5', 'kiwi4', 'kiwi3', 'kiwi2', 'kiwi1'], 
     'dias_mora': [ '-1', '-1', '-1', '-1', '-1', '-1', '-1', '4', '6', '0', '8', '9', '7', '10', '3', '2', '3', '2']  
                  [ '12', '0', '4', '4', '4', '7', '10', '4', '-6', '-7', '8', '8', '17', '10', '10', '-2', '3', '2']
}

df = pd.DataFrame.from_dict(data)  # convert dictionary to dataframe
lob = df['obligacion'].unique().tolist()   # distinct list of first columne
ddall = {}
for o in lob:  # each ob
    ldm = df[df['obligacion']==o]['dias_mora'].tolist()  # filter by ob, convert last column to list

    all = []
    cnt = 0
    for i in range(len(ldm)-1): # each element in list
       cnt  = 1
       if ldm[i] != ldm[i 1]:   # if last element in this sequence
          all.append((ldm[i],cnt))  # append tuple to final list
          cnt = 0
    else:
       all.append((ldm[i 1],cnt 1))  # last element
    ddall[o] = [(int(e[0]),e[1]) for e in list(all)]

print(ddall)
  

Вывод

 {
  200000000123: [(-1, 7), (4, 1), (6, 1), (0, 1), (8, 1), (9, 1), (7, 1), (10, 1), (3, 1), (2, 1), (3, 1), (2, 1)], 
  200000000444: [(12, 1), (0, 1), (4, 3), (7, 1), (10, 1), (4, 1), (-6, 1), (-7, 1), (8, 2), (17, 1), (10, 2), (-2, 1), (3, 1), (2, 1)]
}
  

—- Обновить —-

Согласно документации Pandas, следует избегать итерации по фрейму данных, поскольку это очень медленно. Чтобы ускорить этот скрипт, я преобразовал ключевые столбцы в списки, заархивировал их и повторил над объектом zip. Скрипт выполняется примерно в два раза быстрее. Результат тот же.

Вот более быстрый скрипт:

 import pandas as pd

data = {
     'obligacion': [200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123]  
                   [200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444],
     '0': [ 'mora18', 'mora17', 'mora16', 'mora15', 'mora14', 'mora13', 'mora12', 'mora11', 'mora10', 'mora9', 'mora8', 'mora7', 'mora6', 'mora5', 'mora4', 'mora3', 'mora2', 'mora1']  
          [ 'kiwi18', 'kiwi17', 'kiwi16', 'kiwi15', 'kiwi14', 'kiwi13', 'kiwi12', 'kiwi11', 'kiwi10', 'kiwi9', 'kiwi8', 'kiwi7', 'kiwi6', 'kiwi5', 'kiwi4', 'kiwi3', 'kiwi2', 'kiwi1'], 
     'dias_mora': [ '-1', '-1', '-1', '-1', '-1', '-1', '-1', '4', '6', '0', '8', '9', '7', '10', '3', '2', '3', '2']  
                  [ '12', '0', '4', '4', '4', '7', '10', '4', '-6', '-7', '8', '8', '17', '10', '10', '-2', '3', '2']
}

df = pd.DataFrame.from_dict(data)  # convert dictionary to dataframe

# convert key columns to lists for faster scan
lstob = df['obligacion'].to_list()
lstdm = df['dias_mora'].to_list()

ddall = {}
lastob = "___"  # will delete this entry
lst = []
lastv = cnt = 1
tt = zip(lstob, lstdm)  # combine lists for iteration
for t in tt:  # each ob/dm
   if t[0] != lastob:  # new ob
      lst.append((int(lastv), cnt)) # add last sequence
      ddall[lastob] = lst  # add list to dictionary
      lastob = t[0]
      lst = []
      lastv = t[1]
      cnt = 1
   else:  # same ob
      if t[1] != lastv: # if new dm
         lst.append((int(lastv), cnt))
         lastv = t[1]
         cnt = 1
      else:
         cnt  = 1 # just increment ctr
else: # last row in dataset
   lst.append((int(t[1]), cnt))
   ddall[lastob] = lst

del ddall['___']  # remove temporary entry
print(ddall)
  

—- Обновление # 2 —-

Если вы хотите добавить dias_mora в выходные данные, вы можете собирать записи dm по мере подсчета значений.

Для этого вот обновленный код:

 import pandas as pd

data = {
     'obligacion': [200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123, 200000000123]  
                   [200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444, 200000000444],
     '0': [ 'mora18', 'mora17', 'mora16', 'mora15', 'mora14', 'mora13', 'mora12', 'mora11', 'mora10', 'mora9', 'mora8', 'mora7', 'mora6', 'mora5', 'mora4', 'mora3', 'mora2', 'mora1']  
          [ 'kiwi18', 'kiwi17', 'kiwi16', 'kiwi15', 'kiwi14', 'kiwi13', 'kiwi12', 'kiwi11', 'kiwi10', 'kiwi9', 'kiwi8', 'kiwi7', 'kiwi6', 'kiwi5', 'kiwi4', 'kiwi3', 'kiwi2', 'kiwi1'], 
     'dias_mora': [ '-1', '-1', '-1', '-1', '-1', '-1', '-1', '4', '6', '0', '8', '9', '7', '10', '3', '2', '3', '2']  
                  [ '12', '0', '4', '4', '4', '7', '10', '4', '-6', '-7', '8', '8', '17', '10', '10', '-2', '3', '2']
}

df = pd.DataFrame.from_dict(data)  # convert dictionary to dataframe

# convert key columns to lists for faster scan
lstob = df['obligacion'].to_list()
lst0  = df['0'].to_list()
lstdm = df['dias_mora'].to_list()

cur0 = ""

ddall = {}
lastob = "___"  # will delete this entry
lst = []
lastv = cnt = 1
tt = zip(lstob, lst0, lstdm)  # combine lists for iteration
for t in tt:  # each ob/dm
   if t[0] != lastob:  # new ob
      lst.append((int(lastv), cnt, cur0)) # add last sequence
      ddall[lastob] = lst  # add list to dictionary
      lastob = t[0]
      lst = []
      lastv = t[2]
      cur0 = t[1]
      cnt = 1
   else:  # same ob
      if t[2] != lastv: # if new dm
         lst.append((int(lastv), cnt, cur0))
         lastv = t[2]
         cur0 = t[1]
         cnt = 1
      else:
         cnt  = 1 # just increment ctr
         cur0  = ','   t[1]
else: # last row in dataset
   lst.append((int(t[2]), cnt, cur0))
   ddall[lastob] = lst

del ddall['___']  # remove temporary entry
print(ddall)
  

Вывод (форматированный)

 {
200000000123: [
    (-1, 7, 'mora18,mora17,mora16,mora15,mora14,mora13,mora12'), 
    (4,  1, 'mora11'), 
    (6,  1, 'mora10'), 
    (0,  1, 'mora9'), 
    (8,  1, 'mora8'), 
    (9,  1, 'mora7'), 
    (7,  1, 'mora6'), 
    (10, 1, 'mora5'), 
    (3,  1, 'mora4'), 
    (2,  1, 'mora3'), 
    (3,  1, 'mora2'), 
    (2,  1, 'mora1')], 
200000000444: [
    (12, 1, 'kiwi18'), 
    (0,  1, 'kiwi17'), 
    (4,  3, 'kiwi16,kiwi15,kiwi14'), 
    (7,  1, 'kiwi13'), 
    (10, 1, 'kiwi12'), 
    (4,  1, 'kiwi11'), 
    (-6, 1, 'kiwi10'), 
    (-7, 1, 'kiwi9'), 
    (8,  2, 'kiwi8,kiwi7'), 
    (17, 1, 'kiwi6'), 
    (10, 2, 'kiwi5,kiwi4'), 
    (-2, 1, 'kiwi3'), 
    (3,  1, 'kiwi2'), 
    (2,  1, 'kiwi1')]
}
  

При необходимости список dm может быть преобразован в список с использованием метода string split .

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

1. А что, если значение первого столбца изменяется по строкам, я имею в виду, что данные представляют собой только первые 18 строк из более чем 5000 строк

2. это работает медленно, но работает так, как ожидалось, если есть какой-либо способ пропустить for, я был бы признателен.

3. Потрясающе, работает намного быстрее, я обновил сообщение своим собственным кодом, может быть, вы захотите посмотреть, как я провел подсчет. У меня возникли проблемы с упорядочением результата так, чтобы число, которое повторяется чаще всего, находилось в первой позиции

4. Есть ли способ добавить столбец 0 к кортежу?, чтобы он сохранял первые данные, которые он находит для каждого числа. Например, 200000000123: [(-1, 7, ‘mora18’), (4, 1, ‘mora17’) ……….]. При необходимости я могу открыть новый вопрос.

Ответ №2:

Вы могли бы попробовать этот подход.

 data = {'obligacion':
    { 0: 200000000123,  1: 200000000123,
      2: 200000000123,  3: 200000000123,
      4: 200000000123,  5: 200000000123,
      6: 200000000123,  7: 200000000123,
      8: 200000000123,  9: 200000000123,
     10: 200000000456, 11: 200000000456,
     12: 200000000456, 13: 200000000456,
     14: 200000000456, 15: 200000000456,
     16: 200000000456, 17: 200000000456},
    0:
    { 0: 'mora18', 1: 'mora17',
      2: 'mora16', 3: 'mora15',
      4: 'mora14', 5: 'mora13',
      6: 'mora12', 7: 'mora11',
      8: 'mora10', 9:  'mora9',
     10: 'mora8', 11:  'mora7',
     12: 'mora6', 13: 'mora5',
     14: 'mora4', 15: 'mora3',
     16: 'mora2', 17: 'mora1'},
    'dias_mora':
    { 0: '-1',  1: '0',
      2: '-1',  3: '6',
      4: '-1',  5: '4',
      6: '-1',  7: '4',
      8:  '6',  9: '0',
     10:  '8', 11: '9',
     12:  '7', 13: '10',
     14:  '3', 15: '2',
     16:  '3', 17: '2'}}

df = pd.DataFrame.from_dict(data)  # convert dictionary to dataframe

from collections import defaultdict
d_new = defaultdict(list) #setup a dictionary that can contain lists

#use a crosstab function to count the values for each obligacion
ctab = pd.crosstab(index=df['dias_mora'], columns=df['obligacion'])

#for each  obligacion, get the dias_mora key and counts
for obl,d_m in ctab.items():
    #for each dias_mora key, check if count is > 0. If so, write to dict
    for d_m_key,count_dm in d_m.items():
        if count_dm > 0: d_new[obl].append((d_m_key,count_dm))

#convert defaultdict to normal dict
d_new = dict(d_new)

print (d_new)
  

Результат для этого будет следующим:

 {200000000123: [('-1', 4), ('0', 2), ('4', 2), ('6', 2)], 200000000456: [('10', 1), ('2', 2), ('3', 2), ('7', 1), ('8', 1), ('9', 1)]}
  

Если вы хотите преобразовать ключи в кортеже в числа, то вы можете изменить их, помещая его в список.

 if count_dm > 0: d_new[obl].append((int(d_m_key),count_dm))
  

Это даст вам следующий результат:

 {200000000123: [(-1, 4), (0, 2), (4, 2), (6, 2)], 200000000456: [(10, 1), (2, 2), (3, 2), (7, 1), (8, 1), (9, 1)]}
  

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

1. Программа должна подсчитывать одни и те же числа, которые повторяются последовательно, а не общую сумму, было трудно объяснить эту часть