Pandas dataframe - столбец содержит индекс для других столбцов

У меня есть фрейм данных (df), где один из столбцов («bestcol») содержит индексы других столбцов в таблице. Я хочу захватить столбец, на который ссылается «bestcol», округлить его и создать новый столбец с этой информацией (см. таблицу ниже для грубого примера, в котором bestcol = 1 относится к Val1, 2 относится к Val2, 3 относится к Вал3).

Вал1 Вал2 Вал3 лучший кол Финал 1.1 2.1 3.1 1 1,0 11.1 22.1 33.1 2 22,0 111,1 222,1 333,1 3 333,0

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

bestcol_list = []
for i in range(len(df)):
    bestVal = round(df.iloc[i, df['bestcol'][i]], 0)
    bestcol_list.append(bestVal)
    
df['Final'] = bestcol_list

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

bestcol_list = df.apply(lambda row: round(row[row['bestcol']], 0), axis=1)

df['Final'] = bestcol_list

Это оказалось немного медленнее, чем просто перебор таблицы. Есть ли векторизованный подход к решению этой проблемы, который я не рассматриваю?

Спасибо!

В моих данных несколько миллионов записей — вы имеете в виду, что у вас также есть несколько миллионов столбцов?

RomanPerekhrest 03.04.2023 21:28

Несколько миллионов строк, но также около 100 столбцов, на которые может ссылаться «bestcol».

McGoushie 03.04.2023 21:30

так что в какой-то момент, когда bestcol исчерпывается для первых ~ 100 столбцов, он снова начинается с 1 (для следующих 100 строк)? Это не следует из вашего цикла for

RomanPerekhrest 03.04.2023 21:35

С помощью df.iloc[i, df['bestcol'][i]] я получаю значение из «i-й» строки, а столбец определяется «i-й» строкой в ​​«bestcol» (иначе, df[' bestcol'][i]). Так что не должно быть проблемы выхода за пределы.

McGoushie 03.04.2023 21:46

как вы уже написали, у вас есть «несколько миллионов записей», поэтому код df.iloc[i, df['bestcol'][i]],, скажем, для записи миллиона требует индекса столбца миллиона в bestcol, но вы сказали, что всего около 100 столбцов. это противоречие

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

Ответы 5

Я использовал вложенный np.where для обработки трех условий, а затем np.round.

df['Final'] = np.round(np.where(df.bestcol == 1, df.Val1, np.where(df.bestcol == 2, df.Val2, df.Val3)), 0)
numpy.select, вероятно, было бы легче читать здесь, и его можно было бы автоматически заполнить простым пониманием списка
Paul H 04.04.2023 05:29
Ответ принят как подходящий

Вы можете использовать индексацию numpy:

row = np.arange(len(df))
col = df['bestcol'].values - 1
x = df.filter(like='Val').values  # or df.iloc[:, :3].values

df['Final'] = np.round(x[row, col])

Выход:

>>> df
    Val1   Val2   Val3  bestcol  Final
0    1.1    2.1    3.1        1    1.0
1   11.1   22.1   33.1        2   22.0
2  111.1  222.1  333.1        3  333.0

Производительность для 5_000_000 строк и 100 столбцов:

M = 5_000_000
N = 100
x = np.random.uniform(1, 500, (M, N))
row = np.arange(M)
col = np.random.randint(1, N+1, M) - 1

%timeit np.round(x[row, col])
75.5 ms ± 388 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Простой и быстрый питонический способ:

import pandas as pd

df = pd.DataFrame({'Val1': [1.1, 11.1, 111.1],
                   'Val2': [2.1, 22.1, 222.1],
                   'Val3': [3.1, 33.1, 333.1],
                   'bestcol': [1, 2, 3],
                   })

df['Final'] = [round(df[['Val1', 'Val2', 'Val3']].iloc[i, p]) \
               for i, p in enumerate(df.bestcol.sub(1).tolist())]

print(df)

Результат

    Val1   Val2   Val3  bestcol  Final
0    1.1    2.1    3.1        1      1
1   11.1   22.1   33.1        2     22
2  111.1  222.1  333.1        3    333

Поскольку ваш bestcol содержит упорядоченные порядковые номера фактических столбцов, вы можете применить numpy.diag:

df['Final'] = np.round(np.diag(df[df.columns[:-1]]))

       1      2      3  bestcol  Final
0    1.1    2.1    3.1        1    1.0
1   11.1   22.1   33.1        2   22.0
2  111.1  222.1  333.1        3  333.0

попробуй это:

df['Final'] = df.values[df.index, df.bestcol-1].round(0)
print(df)
>>>
    Val1   Val2   Val3  bestcol  Final
0    1.1    2.1    3.1        1    1.0
1   11.1   22.1   33.1        2   22.0
2  111.1  222.1  333.1        3  333.0

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