У меня есть фрейм данных (df), где один из столбцов («bestcol») содержит индексы других столбцов в таблице. Я хочу захватить столбец, на который ссылается «bestcol», округлить его и создать новый столбец с этой информацией (см. таблицу ниже для грубого примера, в котором bestcol = 1 относится к Val1, 2 относится к Val2, 3 относится к Вал3).
Мой первоначальный подход заключался в циклическом просмотре таблицы построчно:
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
Это оказалось немного медленнее, чем просто перебор таблицы. Есть ли векторизованный подход к решению этой проблемы, который я не рассматриваю?
Спасибо!
Несколько миллионов строк, но также около 100 столбцов, на которые может ссылаться «bestcol».
так что в какой-то момент, когда bestcol
исчерпывается для первых ~ 100 столбцов, он снова начинается с 1
(для следующих 100 строк)? Это не следует из вашего цикла for
С помощью df.iloc[i, df['bestcol'][i]] я получаю значение из «i-й» строки, а столбец определяется «i-й» строкой в «bestcol» (иначе, df[' bestcol'][i]). Так что не должно быть проблемы выхода за пределы.
как вы уже написали, у вас есть «несколько миллионов записей», поэтому код df.iloc[i, df['bestcol'][i]],
, скажем, для записи миллиона требует индекса столбца миллиона в bestcol
, но вы сказали, что всего около 100 столбцов. это противоречие
Я использовал вложенный 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
, вероятно, было бы легче читать здесь, и его можно было бы автоматически заполнить простым пониманием списка
Вы можете использовать индексацию 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
В моих данных несколько миллионов записей — вы имеете в виду, что у вас также есть несколько миллионов столбцов?