#python #pandas
Вопрос:
Я пытаюсь найти способ поместить nan в столбцы, которые не существуют между 2 начальными/конечными значениями в другом столбце для каждой строки. Допустим, у меня есть нижеприведенный фрейм данных:
df = pd.DataFrame({'39' : [1, np.nan, 3], '40' : [2, 4, 5], '41' : [3, 1, 4], '42' : [2, 5, 2], '43' : [1, 1, np.nan], 'start' : [39, 40, 41], 'end' : [41, 41, 43]}) 39 40 41 42 43 start end 0 1.0 2 3 2 1 39 41 1 NaN 4 1 5 1 40 41 2 3.0 5 4 2 3 41 43
Я хочу поместить nan в нумерованные столбцы, которые не находятся между номерами начальных/конечных столбцов (включительно), чтобы получить следующее:
39 40 41 42 43 start end 0 1.0 2.0 3 NaN NaN 39 41 1 NaN 4.0 1 NaN NaN 40 41 2 NaN NaN 4 2.0 3.0 41 43
Единственный способ, которым я могу в настоящее время думать об этом, — это перебирать строки или столбцы, чтобы проверить, находится ли между началом и концом или нет, но я знаю, что перебор кадров данных-плохая практика. Я мог бы превратить столбцы в списки, просмотреть их и переназначить, но мне просто интересно, есть ли более эффективный способ добиться этого?
Редактировать: Я должен отметить, что числовые столбцы-это номера недель, поэтому они могут длиться более года (например, 51, 52, 1, 2, 3, тогда начало может быть 51, а конец может быть 1). Мне интересно, нужно ли мне составить список номеров столбцов, которые нужно сохранить, прежде чем делать это, так как использование lt; или gt; в этом случае не сработает.
Пример этого:
df2 = pd.DataFrame({'51' : [1, np.nan, 3], '52' : [2, 4, 5], '1' : [3, 1, 4], '2' : [2, 5, 2], '3' : [1, 1, 3], 'start' : [51, 52, 52], 'end' : [1, 2, 1]}) 51 52 1 2 3 start end 0 1.0 2 3 2 1 51 1 1 NaN 4 1 5 1 52 2 2 3.0 5 4 2 3 52 1
Выход:
51 52 1 2 3 start end 0 1.0 2 3 NaN NaN 51 1 1 NaN 4 1 5.0 NaN 52 2 2 NaN 5 4 NaN NaN 52 1
Комментарии:
1. Я вижу редактирование, возможно ли изменить образец данных для него? Тогда, похоже, оба решения потерпели неудачу.
2. @jezrael Да, добавлю еще один пример. Извините, забыл добавить эту заметку изначально. Я думаю, может быть, мне нужен список столбцов, которые я буду хранить каждый раз вместо этого
3. хммм, возможное решение должно быть добавлено в год
200151, 200152, 200201, 200202, 200203
, не уверен, возможно ли в вашем решении4. @jezrael да, я мог бы добавить год в имена столбцов, если нет другого способа сделать это. Мне было интересно, могу ли я составить список имен столбцов, а затем начать с соответствующего начала и закончить в соответствующем конце, затем сохранить все, что находится между ними и включая
5. да, это работает, добавил в ответ.
Ответ №1:
Мы можем использовать stack
и unstack
здесь:
df = df.set_index(["start", "end"]).stack() idx = df.index values = idx.get_level_values(2).astype(int) start = idx.get_level_values(0) end = idx.get_level_values(1) df.where((values gt;= start) amp; (values lt;= end)).unstack().reset_index()
start end 39 40 41 42 43 0 39 41 1.0 2.0 3.0 NaN NaN 1 40 41 NaN 4.0 1.0 NaN NaN 2 41 43 NaN NaN 4.0 2.0 NaN
Ответ №2:
Решение Numpy с сравнением между началом и концом :
df.columns = df.columns[:-2].astype(int).tolist() df.columns[-2:].tolist() s = df['start'].to_numpy() e = df['end'].to_numpy() cols = df.columns[:-2].to_numpy() m = (s[:, None] lt;= cols) amp; (e[:, None] gt;= cols) df.iloc[:, :-2] = df.iloc[:, :-2].where(m) print (df) 39 40 41 42 43 start end 0 1.0 2.0 3 NaN NaN 39 41 1 NaN 4.0 1 NaN NaN 40 41 2 NaN NaN 4 2.0 NaN 41 43
ИЗМЕНИТЬ: Если невозможно, сравните по значению с совокупной суммой, например e
, с обратной стороны и проверьте, есть ли 1
в обеих масках:
s = df['start'].astype(str).to_numpy() e = df['end'].astype(str).to_numpy() cols = df.columns[:-2].to_numpy() m1 = np.cumsum((s[:, None] == cols), axis=1) == 1 m2 = np.cumsum((e[:, None] == cols[::-1]), axis=1)[:, ::-1] == 1 m = m1 amp; m2 df.iloc[:, :-2] = df.iloc[:, :-2].where(m) print (df) 51 52 1 2 3 start end 0 1.0 2 3 NaN NaN 51 1 1 NaN 4 1 5.0 NaN 52 2 2 NaN 5 4 NaN NaN 52 1
Комментарии:
1. Это определенно работает на примере, который я привел, но когда я пытаюсь перенести его на свой реальный пример, он просто охватывает все с помощью Nan, и я не совсем уверен, почему, я думаю, что это связано с тем, что s [:, None] являются строками, но имена столбцов являются целыми числами, поэтому они не совпадают?
2. @EmiOB — точно, в моем решении добавлены
.astype(str)
для сопоставления с именами столбцов также строки, возможно, нужно удалить его. Вs = df['start'].astype(str).to_numpy() e = df['end'].astype(str).to_numpy()
том, чтобыs = df['start'].to_numpy() e = df['end'].to_numpy()
Ответ №3:
for column in ['39', '40', '41', '42', '43']: df[column].loc[(float(column) lt; df['start']) | (float(column) gt; df['end'])] = np.NaN
напечатает:
39 40 41 42 43 start end 0 1.0 2.0 3.0 NaN NaN 39 41 1 NaN 4.0 1.0 NaN NaN 40 41 2 NaN NaN 4.0 2.0 NaN 41 43