Pandas: использование слишком большого объема памяти с условным скользящим счетом - PullRequest
0 голосов
/ 16 июня 2020

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

df = pd.DataFrame({'B': ['X', 'X' , 'Y', 'X', 'Y', 'Y', 'X', 'X', 'Y', 'Y', 'X', 'Y'],
                   'group': ["IT", "IT", "IT", "MV", "MV", "MV", "IT", "MV", "MV", "IT", "IT", "MV"]})

for i in df['B'].unique():
    df.loc[df['B']==i, 'count'] = df.where(df['B'].eq(i)).groupby(df['group'])['B'].transform(lambda x: x.rolling(3, min_periods=1).count().shift(fill_value=0))
print(df)

    B group  count
0   X    IT    0.0
1   X    IT    1.0
2   Y    IT    0.0
3   X    MV    0.0
4   Y    MV    0.0
5   Y    MV    1.0
6   X    IT    2.0
7   X    MV    1.0
8   Y    MV    2.0
9   Y    IT    1.0
10  X    IT    1.0
11  Y    MV    2.0

Как указано выше, мы группируемся по «группе» и выполняем скользящий подсчет «X» и «Y» в столбце B с длиной окна = 3. Если «X» 'является текущей строкой, затем мы подсчитываем количество раз, когда' X 'появлялось в предыдущих 3 наблюдениях в группе' group ', не включая счетчик текущей строки (поэтому сдвиньте назад на период = 1).

Однако этот код медленный и использует слишком много памяти при использовании с большими наборами данных. Благодарен за улучшение.



1 Ответ

2 голосов
/ 16 июня 2020

Я разработал другое решение вашего вопроса, основанное на группировке и использовании горячего кодирования (get_dummy).

Вот код:

df = pd.DataFrame({'B': ['X', 'X' , 'Y', 'X', 'Y', 'Y', 'X', 'X', 'Y', 'Y', 'X', 'Y'],
                   'group': ["IT", "IT", "IT", "MV", "MV", "MV", "IT", "MV", "MV", "IT", "IT", "MV"]})

# add a one-hot encoding to the dataframe. 
t = pd.concat([df, pd.get_dummies(df.B)], axis=1)

t.index.name = "inx"

# do a rolling sum of 4. It's the past 3, plus 1. 
t = t.groupby("group").rolling(4, min_periods = 1).sum()
t = t.reset_index().set_index("inx").sort_index()

# remove the extra '1' from the rolling result. 
t.loc[:, ["X", "Y"]] = t.loc[:, ["X", "Y"]] - 1

# merge back the results with the original dataframe. 
t = pd.concat([df, t[["X", "Y"]]], axis=1)

# create a 'count' column which is based on the values of 'B'. 
t["count"] = t.lookup(t.index, t.B )

Результат:

     B group    X    Y  count
inx                          
0    X    IT  0.0 -1.0    0.0
1    X    IT  1.0 -1.0    1.0
2    Y    IT  1.0  0.0    0.0
3    X    MV  0.0 -1.0    0.0
4    Y    MV  0.0  0.0    0.0
5    Y    MV  0.0  1.0    1.0
6    X    IT  2.0  0.0    2.0
7    X    MV  1.0  1.0    1.0
8    Y    MV  0.0  2.0    2.0
9    Y    IT  1.0  1.0    1.0
10   X    IT  1.0  1.0    1.0
11   Y    MV  0.0  2.0    2.0

Все в одной строке:

df['count'] = (pd.concat([df, df['B'].str.get_dummies()], axis=1)
                 .groupby('group')
                 .rolling(4, min_periods=1)
                 .sum()
                 .sort_index(level=1)
                 .reset_index(drop=True)
                 .lookup(df.index, df['B']) - 1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...