Возьмите разницу всех элементов серии с предыдущими в пантах питона - PullRequest
5 голосов
/ 14 мая 2019

У меня есть фрейм данных с отсортированными значениями, помеченными идентификаторами, и я хочу взять разницу значения для первого элемента идентификатора со значением последних элементов всех предыдущих идентификаторов.Код ниже делает то, что я хочу:

import pandas as pd

a = 'a'; b = 'b'; c = 'c'
df = pd.DataFrame(data=[*zip([a, a, a, b, b, c, a], [1, 2, 3, 5, 6, 7, 8])],
                  columns=['id', 'value'])
print(df)
# # take the last value for a particular id
# last_value_for_id = df.loc[df.id.shift(-1) != df.id, :]
# print(last_value_for_id)
current_id = ''; prev_values = {};diffs = {}
for t in df.itertuples(index=False):
    prev_values[t.id] = t.value
    if current_id != t.id:
        current_id = t.id
    else: continue
    for k, v in prev_values.items():
        if k == current_id: continue
        diffs[(k, current_id)] = t.value - v
print(pd.DataFrame(data=diffs.values(), columns=['diff'], index=diffs.keys()))

печатает:

  id  value
0  a      1
1  a      2
2  a      3
3  b      5
4  b      6
5  c      7
6  a      8
     diff
a b     2
  c     4
b c     1
  a     2
c a     1

Однако я хочу сделать это в векторизации.Я нашел способ получить последовательность последних элементов, как в:

# take the last value for a particular id
last_value_for_id = df.loc[df.id.shift(-1) != df.id, :]
print(last_value_for_id)

, что дает мне:

  id  value
2  a      3
4  b      6
5  c      7

, но не могу найти способ использовать это, чтобы взятьотличается в векторизации

Ответы [ 2 ]

4 голосов
/ 14 мая 2019

Мой метод

s=df.groupby(df.id.shift().ne(df.id).cumsum()).agg({'id':'first','value':['min','max']})
s.columns=s.columns.droplevel(0)
t=s['min'].values[:,None]-s['max'].values
t=t.astype(float)

Ниже приведены все формы, чтобы соответствовать вашему выводу

t[np.triu_indices(t.shape[1], 0)] = np.nan
newdf=pd.DataFrame(t,index=s['first'],columns=s['first'])
newdf.values[newdf.index.values[:,None]==newdf.index.values]=np.nan
newdf=newdf.T.stack()
newdf
Out[933]: 
first  first
a      b        2.0
       c        4.0
b      c        1.0
       a        2.0
c      a        1.0
dtype: float64
4 голосов
/ 14 мая 2019

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

# enumerate ids, should be careful
ids = [a,b,c]
num_ids = len(ids)

# compute first and last
f = df.groupby('id').value.agg(['first','last'])

# lower triangle mask
mask = np.array([[i>=j for j in range(num_ids)] for i in range(num_ids)])

# compute diff of first and last, then mask 
diff = np.where(mask, None, f['first'][None,:] - f['last'][:,None])
diff = pd.DataFrame(diff,
                    index = ids,
                    columns = ids)
# stack
diff.stack()

выход:

a  b    2
   c    4
b  c    1
dtype: object

Редактировать для обновления данных:

Для обновленных данных подход аналогичен, если мы можем создать таблицу f:

# create blocks of consecutive id
blocks = df['id'].ne(df['id'].shift()).cumsum()

# groupby
groups = df.groupby(blocks)

# create first and last values
df['fv'] = groups.value.transform('first')
df['lv'] = groups.value.transform('last')

# the above f and ids 
# note the column name change
f = df[['id','fv', 'lv']].drop_duplicates()
ids = f['id'].values
num_ids = len(ids)

Выход:

a   b     2
    c     4
    a     5
b   c     1
    a     2
c   a     1
dtype: object

Если вы хотите пойти дальше и отбросить индекс (a,a), ну, я так ленив: D.

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