Создайте новые столбцы в фрейме данных со значениями из других столбцов в зависимости от условия

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

Type  Price1  Price2  Price3  Price4  Price5  ... ... 
A       nan     1       nan     nan     2           
A       nan     3       nan     nan     2
B       nan     nan     4       5       nan
B       nan     nan     6       7       nan
C       nan     2       nan     nan     1  
C       nan     4       nan     nan     3
D       1       8       nan     nan     nan
D       9       6       nan     nan     nan

Мне нужно преобразовать этот фрейм данных, чтобы он выглядел так:

Type   newcol1    newcol2  ...  ...
     A      2           1
     A      2           3
     B      4           5
     B      6           7
     C      1           2   
     C      3           4   
     D      1           8       
     D      9           6

Это критерии для выбора того, какие столбцы получат какие значения:

if type == 'A'
'Price5' -> 'newcol1' && 'Price2' -> 'newcol2'
if type == 'B'
'Price3' -> 'newcol1' && 'Price4' -> 'newcol2'
if type == 'C'
'Price5' -> 'newcol1' && 'Price2' -> 'newcol2'
if type == 'D'
'Price1' -> 'newcol1' && 'Price2' -> 'newcol2'

Моя логика заключалась в том, чтобы использовать функцию маски для достижения этой цели и присвоить значения двум столбцам, а затем просто переименовать их, что работает, но я обрабатываю каждый случай отдельно и это немного беспорядочно:

df = df.mask(df['Type'].eq('A'), df.assign(**{'Price3': df['Price5'].values, 'Price4': df['Price2'].values}))

df.rename(columns = {'Price3':'newcol1'}, inplace = True) 
df.rename(columns = {'Price4':'newcol2'}, inplace = True)

 Как я могу добиться этого более эффективным способом?

Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
71
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Вы можете использовать собственный groupby.apply с переименованием и переиндексацией:

# dictionary to define the mappings per group
dic = {'A': {'Price5': 'newcol1', 'Price2': 'newcol2'},
       'B': {'Price3': 'newcol1', 'Price4': 'newcol2'},
       'C': {'Price5': 'newcol1', 'Price2': 'newcol2'},
       'D': {'Price1' :'newcol1', 'Price2': 'newcol2'},
       }

def f(g):
    # for each group, rename and select the columns
    d = dic.get(g.name, {})
    return (g.rename(columns=d)
             .reindex(columns=d.values())
           )

# apply the function per group
out = df.groupby('Type').apply(f).reset_index(0)

Выход:

  Type  newcol1  newcol2
0    A      2.0      1.0
1    A      2.0      3.0
2    B      4.0      5.0
3    B      6.0      7.0
4    C      1.0      2.0
5    C      3.0      4.0
6    D      1.0      8.0
7    D      9.0      6.0

Вы можете использовать лямбда-функцию:

df['newCol1'] = df.apply(lambda x: x.Price5 if x.Type == 'A' else (x.Price3 if x.Type == 'B' else (x.Price5 if x.Type == 'C' else (x.Price1 if x.Type=='D' else 'NaN'))), axis=1)
df['newCol2'] = df.apply(lambda x: x.Price2 if x.Type == 'A' else (x.Price4 if x.Type == 'B' else (x.Price2 if x.Type == 'C' else (x.Price2 if x.Type=='D' else 'NA'))), axis=1)
'NaN' и 'NA' не являются числовыми, поэтому они испортят dtype. Используйте np.nan.
wjandrea 10.05.2024 18:52

Вы можете использовать np.select().

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

mapping = pd.DataFrame.from_dict(
    {
        'A': {'newcol1': 'Price5', 'newcol2': 'Price2'},
        'B': {'newcol1': 'Price3', 'newcol2': 'Price4'},
        'C': {'newcol1': 'Price5', 'newcol2': 'Price2'},
        'D': {'newcol1': 'Price1', 'newcol2': 'Price2'}},
    orient='index')
  newcol1 newcol2
A  Price5  Price2
B  Price3  Price4
C  Price5  Price2
D  Price1  Price2

Затем создайте список условий:

cond = [df['Type'].eq(k) for k in mapping.index]

Наконец, используйте np.select():

# For this demo, I'm outputting to a new dataframe:
df_out = df['Type'].to_frame()

for new_col in mapping.columns:
    df_out[new_col] = np.select(
        cond,
        [df[old_col] for old_col in mapping[new_col]]
    ).astype('int')
  Type  newcol1  newcol2
0    A        2        1
1    A        2        3
2    B        4        5
3    B        6        7
4    C        1        2
5    C        3        4
6    D        1        8
7    D        9        6

P.S. pd.Series.case_when() действительно похож на np.select(), но предназначен для замены значений, поэтому я бы не стал использовать его здесь, если только вы не считаете один из old_col значением по умолчанию для каждого new_col.

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

Панды в каждой n-й строке из каждой группы
Сохраняйте столбцы и строки, содержащие «FAIL», в фрейме данных pandas
При приеме данных из CSV в XLSX через Pandas и XLSXwriter возникает ошибка отображения чисел в виде текста
Pandas отображает несколько столбцов с помощью фильтра
Как мне извлечь из xml значение «xml:id» в Python, используя ElementTree, в фрейм данных?
Выбор значений экстремальных температур из столбца фрейма данных pandas, где процесс выбора включает в себя несколько усложняющих условий
Использование pandas read_excel с определенной точностью с плавающей запятой, когда текст присутствует в столбцах/строках данных
Как использовать кусочно/частично .diff() или .pct_change() для строк?
Pandas read_json Будущее предупреждение: поведение to_datetime с unit при синтаксическом анализе строк устарело
Как я могу проверить, имеет ли последняя строка кадра данных метку времени между двумя значениями времени?