Я пытаюсь преобразовать фрейм данных в длинный формат и хочу сохранить записи в соответствии с этим правилом:
(1) если у id есть данные в заглушке - оставить все непустые "длинные" записи, пустые отбросить (2) если у идентификатора нет данных ни в одной из заглушек, сохраните только 1 «длинную» запись об этом
Другими словами, для каждого id
мне нужно вести хотя бы одну запись:
- ровно одна запись, если у id нет данных в заглушке,
-или столько строк, сколько id
заполнил заглушку
Я использую wide_to_long
, потому что он позволяет использовать stubnames
, а melt
— нет (или, по крайней мере, я не мог понять, как его использовать). Это пример кода (вдохновленный официальным документом pandas на wide_to_long
):
import numpy as np
import pandas as pd
np.random.seed(123)
df = pd.DataFrame({"A2001" : {0 : "a", 1 : "b", 2 : "c",3:""},
"A2002" : {0 : "d", 1 : "e", 2 : "",3:""},
"A2003" : {0 : "g", 1 : "h", 2 : "i",3:""},
"A2004" : {0 : "j", 1 : "k", 2 : "l",3:""},
})
df["id"] = df.index
pd.wide_to_long(df, ["A"], i = "id", j = "year")
Результат:
A
id year
0 2001 a
1 2001 b
2 2001 c
3 2001
0 2002 d
1 2002 e
2 2002
3 2002
0 2003 g
1 2003 h
2 2003 i
3 2003
0 2004 j
1 2004 k
2 2004 l
3 2004
Чего мне нужно достичь:
для идентификаторов 0, 1 и 2 мне нужны строки, где ["A"]
пусто (или NaN
);
для id 3 - мне нужно сохранить 1 строку (любую строку, неважно какую):
A
id year
0 2001 a
1 2001 b
2 2001 c
3 2001
0 2002 d
1 2002 e
0 2003 g
1 2003 h
2 2003 i
0 2004 j
1 2004 k
2 2004 l
Я пробовал это:
df.dropna(axis=0,how = "all",subset=["A"])
но он удаляет все строки для идентификатора 3
Редактировать:
Я также ищу общее решение для этого со списком имен заглушек:
df = pd.DataFrame({"A2001" : {0 : "a", 1 : "b", 2 : "",3:""},
"A2002" : {0 : "d", 1 : "e", 2 : "test",3:""},
"A2003" : {0 : "g", 1 : "h", 2 : "i",3:""},
"A2004" : {0 : "j", 1 : "k", 2 : "l",3:""},
"B2001" : {0 : "a", 1 : "b", 2 : "",3:""},
"B2002" : {0 : "d", 1 : "e", 2 : "",3:""},
"B2003" : {0 : "g", 1 : "h", 2 : "i",3:""},
"B2004" : {0 : "j", 1 : "k", 2 : "l",3:""},
})
pd.wide_to_long(df, ["A","B"], i = "id", j = "year")
Мне нужно удалить строки, в которых столбцы A и B пусты, но при этом сохранить хотя бы 1 запись для каждого id
(и ровно одну запись, если A и B оба пусты)
Следуйте своему состоянию и логике
df["id"] = df.index
s=pd.wide_to_long(df, ["A"], i = "id", j = "year")
s=s.groupby(level=0).A.apply(lambda x : x.head(1) if (x=='').all() else x[x!='']).reset_index(level=0,drop=True)
s.sort_index(level=1)
Out[137]:
id year
0 2001 a
1 2001 b
2 2001 c
3 2001
0 2002 d
1 2002 e
0 2003 g
1 2003 h
2 2003 i
0 2004 j
1 2004 k
2 2004 l
Name: A, dtype: object
@horace_vr просто нужно сгруппировать по двум уровням индекса. :-)
Как мне это сделать ? ;)
@horace_vr s=pd.wide_to_long(df, ["A","B"], i = "id", j = "year").stack();s=s.groupby(level=[0,2]).A.apply(lambda x : x.head(1) if (x=='').all() else x[x!='']).reset_index(level=0,drop=True)
'SeriesGroupBy' object has no attribute 'A'
. Наверное, потому что вы его укладываете...?
@horace_vr удалить А s=s.groupby(level=[0,2]).apply(lambda x : x.head(1) if (x=='').all() else x[x!='']).reset_index(level=0,drop=True)
все еще есть 2 пустые записи для идентификатора 3 из-за столбца A/B: A 3 2001 A B 3 2001 B
@horace_vr, если у вас есть следующий вопрос, обновите свой вопрос или откройте новый вопрос.
редактируется с обобщенной проблемой; извините и спасибо
Давайте продолжить обсуждение в чате.
Маска до NaN перед вами wide_to_long
затем dropna
с использованием порога.
m
проверяет, пуст ли каждый столбец-заглушка для строки. Замените либо все, либо все, кроме одного, ''
на NaN на основе m
. Это позволяет нам сохранить ровно одну строку в результате, когда все заглушки отсутствуют:
Если вы измените параметры suffix
или sep
в wide_to_long
, вам нужно будет соответствующим образом настроить создание scols
.
stubs = ['A', 'B']
scols = df.columns[df.columns.str.split('[0-9]+').str[0].isin(stubs)] #Mimic stubs
m = df.loc[:, scols].eq('').all(1)
df.loc[~m, scols] = df.loc[~m, scols].replace('', np.NaN)
df.loc[m, scols[1:]] = np.NaN
(pd.wide_to_long(df.assign(id=df.index), stubnames=stubs, i = "id", j = "year")
.dropna(thresh=1)
.replace(np.NaN, '') #Or perhaps the empty to NaN
)
A B
id year
0 2001 a a
1 2001 b b
3 2001
0 2002 d d
1 2002 e e
2 2002 test
0 2003 g g
1 2003 h h
2 2003 i i
0 2004 j j
1 2004 k k
2 2004 l l
s = pd.wide_to_long(df, ["A"], i = "id", j = "year")
def f(d):
m = d.A.eq('')
return d.head(1) if m.all() else d[~m]
t = pd.concat([f(d) for _, d in s.groupby('id')])
t.loc[[*filter(t.index.__contains__, s.index)]]
A
id year
0 2001 a
1 2001 b
2 2001 c
3 2001
0 2002 d
1 2002 e
0 2003 g
1 2003 h
2 2003 i
0 2004 j
1 2004 k
2 2004 l
Что, если вместо заглушки с одним столбцом у меня есть их список?
pd.wide_to_long(df, ["A","B","C"], i = "id", j = "year")
Можно ли обобщить ваш пример?