Цикл for замедляет производительность. Есть альтернативы?

Я пытаюсь улучшить производительность функции ниже

import numpy as np
import time 

r_0 = 0.1

drt_measurement = 9.999999999999991269e+04  3.305791191233514031e-02
9.083278409243831993e+04    6.686534998595229651e-02
1.569368719222600794e-01    7.259131510582693403e-02
1.425501300345730638e-01    1.423914344911824392e-01
1.294822518377939102e-01    2.296111516784170581e-01
1.176123342498509028e-01    3.236305655613548882e-01
1.068305576352440167e-01    4.110383626909880905e-01
9.703716976156935570e-02    4.774860553090806148e-01
8.814156289893920748e-02    5.096622216542943118e-01
8.006143552369422711e-02    4.972726694760475352e-01
7.272203087054407433e-02    4.349122174992410828e-01
6.605544528827768380e-02    3.237240623831713071e-01
5.999999999999999778e-02    1.727623254500962879e-01


def backcalculate_impedance_from_drt_freq(drt_measurement, r_0):
    
    # Extract frequency and DRT from drt_measurement matrix
    drt_frequency = drt_measurement[:,0]
    drt = drt_measurement[:,1]
    
    # Frequency to angular frequency, ω, and scaling factor
    ω_drt = -np.log(2*np.pi*drt_frequency)
    ds = ω_drt[2] - ω_drt[1] # ds is a constant
    
    # Unscaling the DRT
    unscaled_drt = drt*ds
    
    # Initializing Zreal and ZImag vectors
    ZReal = np.empty((len(drt_frequency),1))
    ZImag = np.empty((len(drt_frequency),1))
    
    print("\nBackImpedance For loop")
    tic = time.time()
    # Solve for ZReal and ZImag
    for i in range(len(drt_frequency)):
        K1 = 1 / (1 + np.exp(2 * (-ω_drt[i] + ω_drt))) * unscaled_drt
        K2 = -np.exp(-ω_drt[i] + ω_drt) * K1
        ZReal[i,0] = sum(K1) + r_0
        ZImag[i,0] = sum(K2)
    
    toc = time.time()
    diff = toc - tic
    print(f'Executed in {diff} seconds or {diff*1000} milliseconds')  
    
    # Save Backcalculated Impedance into a (x,3) matrix
    drt_frequency = drt_frequency[:,None]
    backcalculated_impedance = np.hstack((drt_frequency,ZReal,ZImag))
    
    return backcalculated_impedance

В настоящее время я получаю около 10 мс каждый раз, когда эта функция вызывается, причем большую часть времени приходится на цикл for, что действительно замедляет общее выполнение. Есть ли лучшая альтернатива циклу for или способ векторизации его операций?

Вы должны описать, что вы пробовали до сих пор (особенно в отношении векторизации, которая является подходящим способом и должна работать хорошо).

Jérôme Richard 19.02.2024 18:37

используйте np.sum вместо sum. Я думаю, это ваше узкое место. Кроме того, вы можете использовать line_profiler, чтобы найти узкие места.

dankal444 19.02.2024 18:37

Я использовал np.sum, и он стал в 2 раза быстрее. Переход от ~ 10 мс до ~ 5 мс. В конце концов я воспользовался решением Кристофа и реализовал ваше предложение использовать в нем np.sum. Спасибо @dankal444

impedance_gatto 19.02.2024 20:25
Почему в 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
3
174
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Функцию можно переписать без цикла for, используя векторизацию:

def backcalculate_impedance_from_vectorized(drt_measurement, r_0):
    drt_frequency, drt = drt_measurement[:, 0], drt_measurement[:, 1]
    ω_drt = -np.log(2 * np.pi * drt_frequency)
    ds = ω_drt[2] - ω_drt[1]
    unscaled_drt = drt * ds
    ω_drt_expanded = ω_drt[:, np.newaxis]
    K1 = 1 / (1 + np.exp(2 * (-ω_drt_expanded + ω_drt))) * unscaled_drt
    ZReal = K1.sum(axis=1) + r_0
    ZImag = (-np.exp(-ω_drt_expanded + ω_drt) * K1).sum(axis=1)
    return np.hstack((drt_frequency[:, np.newaxis], ZReal[:, np.newaxis], ZImag[:, np.newaxis]))

Насколько это быстрее?

Christoph 19.02.2024 20:20

Я попробовал ваше решение и изменил сумму на np.sum. Теперь он работает примерно в 10 раз быстрее, чем моя первоначальная версия. Огромное спасибо @Christoph!

impedance_gatto 19.02.2024 20:22

@impedance_gatto, пример Кристофа намного быстрее, чем метод цикла for, но результаты неверны. Сравните полученный результат с результатом исходной функции.

Scott Weiss 19.02.2024 21:27

@ Скотт Вайс. Спасибо, что упомянули об этом. Чтобы получить правильные результаты, код должен иметь «-ω_drt_expanded + ω_drt» вместо «ω_drt_expanded — ω_drt».

impedance_gatto 19.02.2024 21:55

@impedance_gatto, хороший улов. Теперь я вижу, что решение Кристофа правильное после этой замены. Я внес это изменение в его решение.

Scott Weiss 20.02.2024 00:14

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