代码之家  ›  专栏  ›  技术社区  ›  Hasan A Yousef Michael Benjamin

我的信任代码比同等的python代码长得多,结果是错误的

  •  -1
  • Hasan A Yousef Michael Benjamin  · 技术社区  · 6 年前

    转换时 existing Python code 为了生锈,线的数量增加了,有许多类型的变化,最糟糕的是结果都是错误的!我不确定我是否做错了什么,或者是否有优化的方法:

    主要功能是:

    fn main() {
        let series = [
            30, 21, 29, 31, 40, 48, 53, 47, 37, 39, 31, 29, 17, 9, 20, 24, 27, 35, 41, 38, 27, 31, 27,
            26, 21, 13, 21, 18, 33, 35, 40, 36, 22, 24, 21, 20, 17, 14, 17, 19, 26, 29, 40, 31, 20, 24,
            18, 26, 17, 9, 17, 21, 28, 32, 46, 33, 23, 28, 22, 27, 18, 8, 17, 21, 31, 34, 44, 38, 31,
            30, 26, 32,
        ];
    
        triple_exponential_smoothing(&series, 12, 0.716, 0.029, 0.993, 24);
    }
    

    triple_exponential_smoothing 调用另外两个我测试过的函数,它们给出了正确的结果:

    fn initial_trend(series: &[i32], slen: i32) -> f32 {
        let mut sum = 0.0;
        for i in 0..slen as usize { // in Python: for i in range(slen)
            sum += (series[i + slen as usize] as f32 - series[i] as f32) / slen as f32;
        }
        return sum / slen as f32;
    }
    

    这是对python代码的转换:

    def initial_trend(series, slen):
        sum = 0.0
        for i in range(slen):
            sum += float(series[i+slen] - series[i]) / slen
        return sum / slen
    
    # >>> initial_trend(series, 12)
    # -0.7847222222222222
    

    第二个是:

    fn initial_seasonal_components(series: &[i32], slen: i32) -> Vec<f32> {
        let mut seasonals = Vec::new();
        let n_seasons = series.len() as i32 / slen;
        // # compute season averages
        let season_chunks = series //season_averages
                .chunks(slen as usize)
                .collect::<Vec<_>>();
        let season_averages = season_chunks
            .iter()
            .map(|chunk| chunk.iter().sum::<i32>() as f32 / chunk.len() as f32)
            .collect::<Vec<f32>>();
        // # compute initial values
        for i in 0..slen as usize {
            let mut sum_of_vals_over_avg = 0.0;
            for j in 0..n_seasons as usize {
                sum_of_vals_over_avg +=
                    series[i + j * slen as usize] as f32 - season_averages[j] as f32;
            }
            seasonals.push(sum_of_vals_over_avg / n_seasons as f32);
        }
        return seasonals;
    }
    

    这是对python代码的转换:

    def initial_seasonal_components(series, slen):
        seasonals = {}
        season_averages = []
        n_seasons = int(len(series)/slen)
        # compute season averages
        for j in range(n_seasons):
            season_averages.append(sum(series[slen*j:slen*j+slen])/float(slen))
        # compute initial values
        for i in range(slen):
            sum_of_vals_over_avg = 0.0
            for j in range(n_seasons):
                sum_of_vals_over_avg += series[slen*j+i]-season_averages[j]
            seasonals[i] = sum_of_vals_over_avg/n_seasons
        return seasonals
    
    # >>> initial_seasonal_components(series, 12)
    # {0: -7.4305555555555545, 1: -15.097222222222221, 2: -7.263888888888888, 3: -5.097222222222222, 4: 3.402777777777778, 5: 8.069444444444445, 6: 16.569444444444446, 7: 9.736111111111112, 8: -0.7638888888888887, 9: 1.902777777777778, 10: -3.263888888888889, 11: -0.7638888888888887}
    

    此函数中似乎存在错误:

    fn triple_exponential_smoothing(
        series: &[i32],
        slen: i32,
        alpha: f32,
        beta: f32,
        gamma: f32,
        n_preds: i32,
    ) {
        let mut result: Vec<f32> = Vec::new();
        let mut seasonals = initial_seasonal_components(&series, slen);
        println!("The seasonalities are: {:#?}", seasonals);
        let mut smooth = 0.0;
        let mut trend = 0.0;
        // for i in range(len(series)+n_preds):
        for i in 0..(series.len() + n_preds as usize) as usize {
            match i {
                0 => {
                    // # initial values
                    smooth = series[0] as f32;
                    trend = initial_trend(&series, slen);
                    println!("The initial_trend is: {:#?}", trend);
                    result.push(series[0] as f32);
                }
                i if i >= series.len() => {
                    // # we are forecasting
                    let m = i - series.len() + 1;
                    result.push(
                        (smooth as usize + m * trend as usize) as f32 + seasonals[i % slen as usize],
                    )
                }
                _ => {
                    let val = series[i];
                    let last_smooth = smooth;
                    smooth = alpha * (val as f32 - seasonals[i % slen as usize])
                        + (1.0 - alpha) * (smooth + trend);
                    trend = beta * (smooth - last_smooth) + (1.0 - beta) * trend;
                    seasonals[i % slen as usize] = gamma * (val as f32 - smooth)
                        + (1 - gamma as usize) as f32 * seasonals[i % slen as usize];
                    result.push(smooth + trend + seasonals[i % slen as usize]);
                }
            }
        }
        println!("The forecast is: {:#?}", result);
    }
    

    这是对python代码的转换:

    def triple_exponential_smoothing(series, slen, alpha, beta, gamma, n_preds):
        result = []
        seasonals = initial_seasonal_components(series, slen)
        for i in range(len(series)+n_preds):
            if i == 0: # initial values
                smooth = series[0]
                trend = initial_trend(series, slen)
                result.append(series[0])
                continue
            if i >= len(series): # we are forecasting
                m = i - len(series) + 1
                result.append((smooth + m*trend) + seasonals[i%slen])
            else:
                val = series[i]
                last_smooth, smooth = smooth, alpha*(val-seasonals[i%slen]) + (1-alpha)*(smooth+trend)
                trend = beta * (smooth-last_smooth) + (1-beta)*trend
                seasonals[i%slen] = gamma*(val-smooth) + (1-gamma)*seasonals[i%slen]
                result.append(smooth+trend+seasonals[i%slen])
        return result
    
    # # forecast 24 points (i.e. two seasons)
    # >>> triple_exponential_smoothing(series, 12, 0.716, 0.029, 0.993, 24)
    # [30, 20.34449316666667, 28.410051892109554, 30.438122252647577, 39.466817731253066, ...
    

    My complete code is available in the playground

    我非常感谢您对优化代码和修复错误的任何评论。

    2 回复  |  直到 6 年前
        1
  •  3
  •   mcarton    6 年前

    在铁锈中,你把一切都变成 usize 总是:

    (1 - gamma as usize) as f32
    

    如果你想到那个, (1 - gamma as usize) 只有永远 0 1 取决于 gamma . 如果你改为

    (1.0 - gamma) as f32
    

    也会改变

    (smooth as usize + m * trend as usize) as f32
    

    (smooth + m as f32 * trend)
    

    然后得到与Python相同的结果。

    至于性能,这看起来是正确的,但是您可以引入一些临时变量来避免总是重新计算相同的东西(尽管优化器应该有所帮助)。rust的默认编译模式是debug,请务必切换到release进行基准测试。

        2
  •  0
  •   Hasan A Yousef Michael Benjamin    6 年前

    我在这里发布了我收到的最好的评论和回答,希望对其他人有用:

    尝试最小化(并且可能减少到零)代码中的as-cast数。into()/from()并尝试_from()帮助;

    尝试用迭代器替换一些原始循环;

    三倍指数平滑函数有一些参数在调用点很容易混淆,因为rust目前没有命名参数。为了避免这个问题,您可以尝试在结构/元组中打包一些参数。

    在rust函数末尾使用return并不是很惯用。

    另外,值得注意的是,在python中,浮点类型是双浮点,在rust中是f64。这可能会导致精度上的微小差异,尽管可能没有重大差异。

    替换的矿井的功能性整洁代码就在这里。 PlayGound :

    fn main() {
        let series = [
            30,21,29,31,40,48,53,47,37,39,31,29,17,9,20,24,27,35,41,38,
            27,31,27,26,21,13,21,18,33,35,40,36,22,24,21,20,17,14,17,19,
            26,29,40,31,20,24,18,26,17,9,17,21,28,32,46,33,23,28,22,27,
            18,8,17,21,31,34,44,38,31,30,26,32];
    
        triple_exponential_smoothing(&series, 12, 0.716, 0.029, 0.993, 24);
    }
    
    fn initial_trend(series: &[i32], slen: usize) -> f32 {
        series[..slen].iter().zip(&series[slen..])
            .map(|(&a, &b)| (b as f32 - a as f32) / slen as f32).sum::<f32>() / slen as f32
    }
    
    fn initial_seasonal_components (series: &[i32], slen: usize) -> Vec<f32> {
        let n_seasons = series.len() / slen;
        // # compute season averages
        let season_averages = series //season_averages
            .chunks(slen)
            .map(|chunk| chunk.iter().sum::<i32>() as f32 / chunk.len() as f32)
            .collect::<Vec<f32>>();
        // # compute initial values
        (0..slen).map(|i| {
            let mut sum_of_vals_over_avg = 0.0;
            for j in 0..n_seasons {
                sum_of_vals_over_avg += series[i + j * slen] as f32 - season_averages[j] as f32;
            }
            sum_of_vals_over_avg / n_seasons as f32
        }).collect()
    }
    
    fn triple_exponential_smoothing(series: &[i32], slen: usize, alpha: f32, beta: f32,
                                    gamma: f32, n_preds: usize) {
        let mut result: Vec<f32> = Vec::new();
        let mut seasonals = initial_seasonal_components(&series, slen);
        println!("The seasonalities are: {:#?}", seasonals);
        let mut smooth = 0.0;
        let mut trend = 0.0;
    
        for i in 0..(series.len() + n_preds) {
            match i {
                0 => {  // # initial values
                    smooth = series[0] as f32;
                    trend = initial_trend(&series, slen);
                    println!("The initial_trend is: {:#?}", trend);
                    result.push(series[0] as f32);
                },
                i if i >= series.len() => {  // # we are forecasting
                    let m = i - series.len() + 1;
                    result.push((smooth + m as f32 * trend) + seasonals[i % slen])
                },
                _ => {
                    let val = series[i];
                    let last_smooth = smooth;
                    smooth = alpha * (val as f32 - seasonals[i % slen]) +
                        (1.0 - alpha)*(smooth + trend);
                    trend = beta * (smooth - last_smooth) + (1.0 - beta) * trend;
                    seasonals[i % slen] = gamma * (val as f32 - smooth) +
                        (1.0 - gamma) * seasonals[i % slen];
                    result.push(smooth + trend + seasonals[i % slen]);
                }
            }
        }
        println!("The forecast is: {:#?}", result);
    }