Добавлять строки в группы в пандах - PullRequest
0 голосов
/ 25 мая 2018

Я пытаюсь добавить количество строк NaN к каждой группе в кадре данных панд.По сути, я хочу дополнить каждую группу длиной 5 строк.Заказ важен.У меня есть:

    Rank id
0   1  a
1   2  a
2   3  a
3   4  a
4   5  a
5   1  c
6   2  c
7   1  e
8   2  e
9   3  e

Я хочу:

    Rank id
0   1    a
1   2    a
2   3    a
3   4    a
4   5    a
5   1    c
6   2    c
7   NaN  c
8   NaN  c
9   NaN  c
10  1    e
11  2    e
12  3    e
13  NaN  e
14  NaN  e

Ответы [ 6 ]

0 голосов
/ 22 мая 2019

отличных ответов.У меня была другая идея, потому что она больше подходит к проблеме, с которой я имею дело, используя внешнее соединение и pd.merge.В дополнение к примеру, приведенному выше, у меня есть несколько метрических столбцов (в этом примере m1 и m2), которые я хочу установить в ноль для каждой группы, которая не содержит этих значений Rank.в моем случае Ранг - это просто измерение времени, и я df содержит временные ряды по нескольким идентификаторам.

df = pd.read_csv(pd.compat.StringIO("""Rank ID m1 m2
0   1  a 1 3
1   2  a 2 3
2   3  a 1 2
3   4  a 1 3
4   5  a 2 3
6   1  c 2 2
7   2  c 2 4
8   1  e 1 3
9   2  e 1 4
10  3  e 1 2"""), sep=r' +')

Затем я определяю df, содержащий все ранги, в этом примере от 1 до 10.

df_outer_right = pd.DataFrame({'Rank':np.arange(1,11,1)})

наконец, я группирую начальный df по ID и применяю внешнее объединение, используя pd.merge для каждой группы.

df.groupby('ID').apply(lambda df: pd.merge(df, df_outer_right, how='outer', on='Rank'))

, что приводит к:

ID  Rank    ID  m1  m2
a   0   1   a   1.0 3.0
a   1   2   a   2.0 3.0
a   2   3   a   1.0 2.0
a   3   4   a   1.0 3.0
a   4   5   a   2.0 3.0
a   5   6   NaN NaN NaN
a   6   7   NaN NaN NaN
a   7   8   NaN NaN NaN
a   8   9   NaN NaN NaN
a   9   10  NaN NaN NaN
c   0   1   c   2.0 2.0
c   1   2   c   2.0 4.0
c   2   3   NaN NaN NaN
c   3   4   NaN NaN NaN
c   4   5   NaN NaN NaN
c   5   6   NaN NaN NaN
c   6   7   NaN NaN NaN
c   7   8   NaN NaN NaN
c   8   9   NaN NaN NaN
c   9   10  NaN NaN NaN
e   0   1   e   1.0 3.0
e   1   2   e   1.0 4.0
e   2   3   e   1.0 2.0
e   3   4   NaN NaN NaN
e   4   5   NaN NaN NaN
e   5   6   NaN NaN NaN
e   6   7   NaN NaN NaN
e   7   8   NaN NaN NaN
e   8   9   NaN NaN NaN
e   9   10  NaN NaN NaN

Я уверен, что это может быть не самое быстрое решение:)

0 голосов
/ 25 мая 2018

concat и reindex

Это решение не учитывает значения в столбце Rank и добавляет дополнительные строки только при необходимости.

pd.concat([
    d.reset_index(drop=True).reindex(range(5)).assign(id=n)
    for n, d in df.groupby('id')
], ignore_index=True)

    Rank id
0    1.0  a
1    2.0  a
2    3.0  a
3    4.0  a
4    5.0  a
5    1.0  c
6    2.0  c
7    NaN  c
8    NaN  c
9    NaN  c
10   1.0  e
11   2.0  e
12   3.0  e
13   NaN  e
14   NaN  e

Тот же ответ сформулирован немного по-другому

f = lambda t: t[1].reset_index(drop=True).reindex(range(5)).assign(id=t[0])
pd.concat(map(f, df.groupby('id')), ignore_index=True)

factorize

Это решение дает декартово произведение уникальных значений из id и Rank

i, r = df.id.factorize()
j, c = df.Rank.factorize()
b = np.empty((r.size, c.size))
b.fill(np.nan)
b[i, j] = df.Rank.values

pd.DataFrame(dict(Rank=b.ravel(), id=r.repeat(c.size)))

    Rank id
0    1.0  a
1    2.0  a
2    3.0  a
3    4.0  a
4    5.0  a
5    1.0  c
6    2.0  c
7    NaN  c
8    NaN  c
9    NaN  c
10   1.0  e
11   2.0  e
12   3.0  e
13   NaN  e
14   NaN  e
0 голосов
/ 25 мая 2018

Использование pd.crosstab:

df = pd.crosstab(df.Rank, df.ID).iloc[:5].unstack().reset_index()
df.loc[(df[0]==0),'Rank'] = np.nan
del df[0]

Вывод:

   ID  Rank
0   a   1.0
1   a   2.0
2   a   3.0
3   a   4.0
4   a   5.0
5   c   1.0
6   c   2.0
7   c   NaN
8   c   NaN
9   c   NaN
10  e   1.0
11  e   2.0
12  e   3.0
13  e   NaN
14  e   NaN

Другой подход, предполагая, что максимальный размер группы в df равен ровно 5.

In [251]: df.groupby('ID').Rank.apply(np.array).apply(pd.Series).stack(dropna=False)
Out[251]: 
ID
a   0    1.0
    1    2.0
    2    3.0
    3    4.0
    4    5.0
c   0    1.0
    1    2.0
    2    NaN
    3    NaN
    4    NaN
e   0    1.0
    1    2.0
    2    3.0
    3    NaN
    4    NaN
dtype: float64

Полное объяснение:

import pandas as pd
import numpy as np

df = pd.read_csv(pd.compat.StringIO("""Rank ID
0   1  a
1   2  a
2   3  a
3   4  a
4   5  a
6   1  c
7   2  c
8   1  e
9   2  e
10  3  e"""), sep=r' +')

df = pd.crosstab(df.Rank, df.ID).iloc[:5].T.stack().reset_index()
df.loc[(df[0]==0),'Rank'] = np.nan
del df[0]

# pd.crosstab(df.Rank, df.ID) produces:

# ID    a  c  e
# Rank
# 1.0   1  1  1
# 2.0   1  1  1
# 3.0   1  0  1
# 4.0   1  0  0
# 5.0   1  0  0

# applying .T.stack().reset_index() yields:

   # ID  Rank  0
# 0   a   1.0  1
# 1   a   2.0  1
# 2   a   3.0  1
# 3   a   4.0  1
# 4   a   5.0  1
# 5   c   1.0  1
# 6   c   2.0  1
# 7   c   3.0  0
# 8   c   4.0  0
# 9   c   5.0  0
# 10  e   1.0  1
# 11  e   2.0  1
# 12  e   3.0  1
# 13  e   4.0  0
# 14  e   5.0  0

# finally, use df[0] to filter df['Rank']
0 голосов
/ 25 мая 2018

Вы можете использовать частоту идентификаторов и pd.concat для объединения повторений, т.е.

di = (5-df.groupby('id').size()).to_dict()

temp = pd.concat([pd.DataFrame({
                'Rank':np.nan,
                'id': pd.Series(np.repeat(i,di[i]))
                }) for i in df['id'].unique()])

ndf = pd.concat([df,temp],ignore_index=True).sort_values('id')

    Rank id
0    1.0  a
1    2.0  a
2    3.0  a
3    4.0  a
4    5.0  a
5    1.0  c
6    2.0  c
10   NaN  c
11   NaN  c
12   NaN  c
7    1.0  e
8    2.0  e
9    3.0  e
13   NaN  e
14   NaN  e
0 голосов
/ 25 мая 2018

Вот один из способов использования одного pd.DataFrame.append, следующего за sort_values.

from itertools import chain

counts = df.groupby('id')['Rank'].count()

lst = list(chain.from_iterable([[np.nan, i]]*(5-c) for i, c in counts.items()))

res = df.append(pd.DataFrame(lst, columns=df.columns))\
        .sort_values(['id', 'Rank'])\
        .reset_index(drop=True)

print(res)

    Rank id
0    1.0  a
1    2.0  a
2    3.0  a
3    4.0  a
4    5.0  a
5    1.0  c
6    2.0  c
7    NaN  c
8    NaN  c
9    NaN  c
10   1.0  e
11   2.0  e
12   3.0  e
13   NaN  e
14   NaN  e
0 голосов
/ 25 мая 2018

Одним из возможных решений является создание помощника DataFrame на numpy.repeat, а затем append к оригиналу, последний sort_values:

s = (5 - df['id'].value_counts())
df = (df.append(pd.DataFrame({'id':np.repeat(s.index, s.values), 'Rank':np.nan}))
       .sort_values('id')
       .reset_index(drop=True))
print (df)
    Rank id
0    1.0  a
1    2.0  a
2    3.0  a
3    4.0  a
4    5.0  a
5    1.0  c
6    2.0  c
7    NaN  c
8    NaN  c
9    NaN  c
10   1.0  e
11   2.0  e
12   3.0  e
13   NaN  e
14   NaN  e

Другое решение, при котором сортировка невозможна, это groupby с пользовательской функцией и append:

def f(x):
    return x.append(pd.DataFrame([[np.nan, x.name]] * (5 - len(x)), columns=['Rank','id']))
df = df.groupby('id', sort=False).apply(f).reset_index(drop=True)
print (df)
   Rank id
0     1  a
1     2  a
2     3  a
3     4  a
4     5  a
5     1  c
6     2  c
7   NaN  c
8   NaN  c
9   NaN  c
10    1  e
11    2  e
12    3  e
13  NaN  e
14  NaN  e
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...