размещение Nan в столбцах, которые не находятся в пределах начальных/конечных номеров для каждой строки

#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