Pandas wide_to_long - сохранить строки со значениями в i или только одну строку с пустым

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

(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 оба пусты)

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
418
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Следуйте своему состоянию и логике

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

Что, если вместо заглушки с одним столбцом у меня есть их список? pd.wide_to_long(df, ["A","B","C"], i = "id", j = "year") Можно ли обобщить ваш пример?

horace_vr 29.05.2019 17:16

@horace_vr просто нужно сгруппировать по двум уровням индекса. :-)

BENY 29.05.2019 17:19

Как мне это сделать ? ;)

horace_vr 29.05.2019 17:30

@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)

BENY 29.05.2019 17:39
'SeriesGroupBy' object has no attribute 'A'. Наверное, потому что вы его укладываете...?
horace_vr 29.05.2019 17:49

@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)

BENY 29.05.2019 17:50

все еще есть 2 пустые записи для идентификатора 3 из-за столбца A/B: A 3 2001 A B 3 2001 B

horace_vr 29.05.2019 18:04

@horace_vr, если у вас есть следующий вопрос, обновите свой вопрос или откройте новый вопрос.

BENY 29.05.2019 18:06

редактируется с обобщенной проблемой; извините и спасибо

horace_vr 29.05.2019 18:20

Давайте продолжить обсуждение в чате.

horace_vr 29.05.2019 18:21
Ответ принят как подходящий

Маска до 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

Похоже на WeNYoBen

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

Другие вопросы по теме