将existing Python code转换为Rust时,行数增加,类型更改很多,最糟糕的是结果错误!我不确定自己是在做错什么,还是有办法对其进行优化:
主要功能是:
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
我感谢您为优化代码和修复错误所做的任何评论。
答案 0 :(得分:3)
在Rust中,您一直将所有内容都转换为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的默认编译模式是调试,请确保切换到基准测试版本。
答案 1 :(得分:0)
我在这里发布我收到的最佳评论和答案,希望对其他人有用:
尝试最小化(可能减少到零)代码中的“ as”强制转换数量。 into()/ from()和try_from()帮助;
尝试用迭代器替换一些原始循环;
Triple_exponential_smoothing函数具有一些易于在调用点混淆的参数,因为Rust目前没有命名参数。为了避免出现此问题,您可以尝试在struct / tuple中打包一些参数。
在Rust函数的末尾使用“返回”并不是很多习惯。
此外,值得注意的是,在Python中,浮点类型是double浮点,在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);
}