线性模拟动态续费优惠

标题

go最小二乘法线性模拟动态续费优惠

dayfee
110
7100
30360
90630
2001200
3651800
package curvefit

import (
	"fmt"
	"github.com/wonderivan/logger"
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
	"os"
	"strconv"
	"time"

	//"log"
	"math"
	//"os"

	"github.com/go-gota/gota/dataframe"
	//"github.com/go-gota/gota/series"
	"gonum.org/v1/gonum/optimize"
	//"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	//"gonum.org/v1/plot/plotutil"
	//"gonum.org/v1/plot/vg"
)

var (
	ColNamesBaicycle           = []string{"day", "fee"}
	OneYearDay                 = 360
	DayFeeSeries_GlobalVarible DayFeePlotter
)

type DayFeePlotter struct {
	XY   plotter.XYs
	args []float64
}

type DayFeeCalc struct {
	RoundRideDay  int64   // 本轮骑行天数
	StartRoundDay string  // 开启本轮优惠日期 example:20210827
	Time          string  // 当前日期,可以不传 example:20210827
	ExpDayFeeLast float64 // 连续RoundRideDay-1 天优惠金额 不传则必须设置DayFeeSeries_GlobalVarible
	ExpDayFee     float64 // 连续RoundRideDay天 优惠金额 不传则必须设置DayFeeSeries_GlobalVarible
}

func NewDataFrame(stringSlice [][]string) dataframe.DataFrame {
	return dataframe.LoadRecords(stringSlice)
}

func LoadCSV(path string) dataframe.DataFrame {
	clsData, err := os.Open(path)
	if err != nil {
		logger.Info(err)
	}

	defer clsData.Close()
	clsDF := dataframe.ReadCSV(clsData)
	// 数据预处理
	clsDF = clsDF.Select(ColNamesBaicycle)

	return clsDF
}

// 抛物线
func OptimizeParabola(clsDF *dataframe.DataFrame) (actPoints, expPoints plotter.XYs, fa, fb float64) {
	// 开始数据拟合

	// 实际观测点
	actPoints = plotter.XYs{}
	// N行数据产生N个点
	for i := 0; i < clsDF.Nrow(); i++ {
		day := clsDF.Elem(i, 0).Val().(int)
		fee := clsDF.Elem(i, 1).Val().(int)

		actPoints = append(actPoints, plotter.XY{
			X: float64(day),
			Y: float64(fee),
		})
	}
	fmt.Println("actPoints is", actPoints)
	result, err := optimize.Minimize(optimize.Problem{
		Func: func(x []float64) float64 {
			//if len(x) != 2 {
			//	panic("illegal x")
			//}
			a := x[0]
			b := x[1]
			var sum float64
			for _, point := range actPoints {
				y := a*point.X*point.X + b*point.X
				sum += math.Pow(math.Abs(y-point.Y), 2)
			}
			fmt.Println("sum is:", sum)
			return sum
		},
	}, []float64{0, 0}, &optimize.Settings{}, &optimize.NelderMead{})
	if err != nil {
		panic(err)
	}

	// 最小二乘法拟合出来的k和b值
	fa, fb = result.X[0], result.X[1]
	expPoints = plotter.XYs{}
	for i := 0; i < OneYearDay; i++ {
		day := i
		x := float64(day)
		expPoints = append(expPoints, plotter.XY{
			X: x,
			Y: fa*x*x + fb*x,
		})
	}
	return
}

//正弦函数
func OptimizeSin(clsDF *dataframe.DataFrame) (actPoints, expPoints plotter.XYs, fa float64) {
	// 开始数据拟合

	// 实际观测点
	actPoints = plotter.XYs{}
	// N行数据产生N个点
	for i := 0; i < clsDF.Nrow(); i++ {
		day := clsDF.Elem(i, 0).Val().(int)
		fee := clsDF.Elem(i, 1).Val().(int)

		actPoints = append(actPoints, plotter.XY{
			X: float64(day),
			Y: float64(fee),
		})
	}
	fmt.Println("actPoints is", actPoints)
	result, err := optimize.Minimize(optimize.Problem{
		Func: func(x []float64) float64 {
			//if len(x) != 2 {
			//	panic("illegal x")
			//}
			a := x[0]
			var sum float64
			for _, point := range actPoints {
				y := a * math.Sin(point.X*math.Pi/(2*float64(OneYearDay)))
				sum += math.Pow(math.Abs(y-point.Y), 2)
			}
			return sum
		},
	}, []float64{0}, &optimize.Settings{}, &optimize.NelderMead{})
	if err != nil {
		panic(err)
	}

	// 最小二乘法拟合出来的k和b值
	fa = result.X[0]
	expPoints = plotter.XYs{}
	for i := 0; i < OneYearDay; i++ {
		day := i
		x := float64(day)
		expPoints = append(expPoints, plotter.XY{
			X: x,
			Y: fa * math.Sin(x*math.Pi/(2*float64(OneYearDay))),
		})
	}
	return
}

// 3次方程
func OptimizeGrade3(clsDF *dataframe.DataFrame) (actPoints, expPoints plotter.XYs, fa, fb, fc float64) {
	// 开始数据拟合

	// 实际观测点
	actPoints = plotter.XYs{}
	// N行数据产生N个点
	for i := 0; i < clsDF.Nrow(); i++ {
		day := clsDF.Elem(i, 0).Val().(int)
		fee := clsDF.Elem(i, 1).Val().(int)

		actPoints = append(actPoints, plotter.XY{
			X: float64(day),
			Y: float64(fee),
		})
	}
	fmt.Println("actPoints is", actPoints)
	result, err := optimize.Minimize(optimize.Problem{
		Func: func(x []float64) float64 {
			//if len(x) != 2 {
			//	panic("illegal x")
			//}
			a := x[0]
			b := x[1]
			c := x[2]

			var sum float64
			for _, point := range actPoints {
				y := a*point.X*point.X*point.X + b*point.X*point.X + c*point.X
				sum += math.Pow(math.Abs(y-point.Y), 2)
			}
			return sum
		},
	}, []float64{0, 0, 0}, &optimize.Settings{}, &optimize.NelderMead{})
	if err != nil {
		panic(err)
	}

	// 最小二乘法拟合出来的k和b值
	fa, fb, fc = result.X[0], result.X[1], result.X[2]
	expPoints = plotter.XYs{}
	for i := 0; i < OneYearDay; i++ {
		day := i
		x := float64(day)
		expPoints = append(expPoints, plotter.XY{
			X: x,
			Y: fa*x*x*x + fb*x*x + fc*x,
		})
	}
	return
}


func dataPlot2(actPoints, expPoints1, expPoints2, expPoints3, expPoints4 plotter.XYs) {
	plt, err := plot.New()
	if err != nil {
		panic(err)
	}
	plotutil.AddScatters(plt, actPoints)

	if err := plotutil.AddLinePoints(plt,
		"paowuxian", expPoints1,
		"zhengxian", expPoints2,
		"3cifang", expPoints3,
		"zhixian", expPoints4,
		"actPoints", actPoints,
	); err != nil {
		panic(err)
	}

	if err := plt.Save(10*vg.Inch, 10*vg.Inch, "classification-fit.png"); err != nil {
		panic(err)
	}
}

func DayFee(expPoints plotter.XYs) plotter.XYs {
	newExpPoints := plotter.XYs{}
	for i, xy := range expPoints {
		y, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", xy.Y), 64)
		if i > 0 {
			y, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", xy.Y-expPoints[i-1].Y), 64)
		}
		newExpPoints = append(newExpPoints, plotter.XY{
			X: xy.X,
			Y: y,
		})
	}
	return newExpPoints
}

func SetDayFee(expPoints plotter.XYs, args []float64) {
	DayFeeSeries_GlobalVarible.XY = expPoints
	DayFeeSeries_GlobalVarible.args = args
}

func GetDayFee(day int64) (float64, error) {
	for _, xy := range DayFeeSeries_GlobalVarible.XY {
		if xy.X == float64(day) {
			return xy.Y, nil
		}
	}
	return 0.00, fmt.Errorf("没有对应的数据")
}

func GetArgs() []float64 {
	return DayFeeSeries_GlobalVarible.args
}

func DataPlot(actPoints, expPoints1 plotter.XYs) {
	plt, err := plot.New()
	if err != nil {
		panic(err)
	}
	plotutil.AddScatters(plt, actPoints)

	if err := plotutil.AddLinePoints(plt,
		"expPoints", expPoints1,
		"actPoints", actPoints,
	); err != nil {
		panic(err)
	}

	if err := plt.Save(10*vg.Inch, 10*vg.Inch, "classification-fit.png"); err != nil {
		panic(err)
	}
}

func DataPlot2(actPoints, expPoints1, expPoints2, expPoints3, expPoints4 plotter.XYs) {
	plt, err := plot.New()
	if err != nil {
		panic(err)
	}
	plotutil.AddScatters(plt, actPoints)

	if err := plotutil.AddLinePoints(plt,
		"paowuxian", expPoints1,
		"zhengxian", expPoints2,
		"3cifang", expPoints3,
		"zhixian", expPoints4,
		"actPoints", actPoints,
	); err != nil {
		panic(err)
	}

	if err := plt.Save(10*vg.Inch, 10*vg.Inch, "classification-fit.png"); err != nil {
		panic(err)
	}
}

//
func (s *DayFeeCalc) OptimizeDayFee() float64 {
	if s.Time == "" {
		s.Time = getLocalData()
	}
	if s.RoundRideDay < 2  {
		fee, _ := GetDayFee(1)
		return fee
	}

	if s.ExpDayFeeLast == 0 {
		fee, err := GetDayFee(s.RoundRideDay - 1)
		if err != nil {
			logger.Error("error is:", err)
		}
		s.ExpDayFeeLast =  fee
	}
	if s.ExpDayFee == 0 {
		fee, err := GetDayFee(s.RoundRideDay)
		if err != nil {
			logger.Error("error is:", err)
		}
		s.ExpDayFee =  fee
	}
	dayFee := s.ExpDayFeeLast
	expDayFee := s.ExpDayFee
	roundPassDay := (day2sec(s.Time) - day2sec(s.StartRoundDay)) / 86400 + 1
	return decimalValue(dayFee-(dayFee-expDayFee)*float64(s.RoundRideDay)/float64(roundPassDay), "2")
}

//浮点数位数
func decimalValue(value float64, num string) float64 {
	value, _ = strconv.ParseFloat(fmt.Sprintf("%."+num+"f", value), 64)
	return value
}

// 获取当天凌晨时间
func getLocalDayZero() int64 {
	timeStr := time.Now().Format("20060102")
	t, _ := time.ParseInLocation("20060102", timeStr, time.Local)
	timeNumber := t.Unix()
	return timeNumber
}

func getLocalData() string {
	timeStr := time.Now().Format("20060102")
	return timeStr
}

func day2sec(timeStr string) int64 {
	t, _ := time.ParseInLocation("20060102", timeStr, time.Local)
	timeNumber := t.Unix()
	return timeNumber
}

示例:

package curvefit

import (
	"gonum.org/v1/plot/plotter"
	"math"
	"time"
	"fmt"
)

func user_example() {

	c := [][]string{
		{"1", "10"},
		{"7", "100"},
		{"30", "360"},
		{"90", "630"},
		{"200", "1200"},
		{"360", "1800"},
	}//{"天数",“预估总金额”}
	data := NewDataFrame(c)
	actPoints, expPoints, fa, fb  := OptimizeParabola(&data) //fa,fb为抛物线公式参数,actPoints为真实点即c中的数据集
	// expPoints为day天的预估总金额数据集 {day, f(day)}
	// f(day) = fa * day * day + fb * day 获取day天的预估总金额
	// f(day-1) = fa * (day-1) * (day-1) + fb * (day-1) 获取day-1天的预估总金额
	//实际当天收取f(day) - f(day-1)

	//newExpPoints := dayfee.DayFee(expPoints)  //返回每天的预估金额,保留2位小数 {day, f(day) - f(day-1)}

	newExpPoints := DayFee(expPoints)

	SetDayFee(newExpPoints, []float64{fa, fb}) // 全局保存

	fmt.Println(GetArgs())
	dayfeee, _ := GetDayFee(1)
	fmt.Println("dayfeee", dayfeee) // 4是本轮使用天数

	d := DayFeeCalc{
		RoundRideDay: 2,
		StartRoundDay : "20200821",
		Time: "20200823",
	}

	fmt.Println(d.OptimizeDayFee())

	sum := dayfeee
	expPointst := plotter.XYs{
		{1.00, dayfeee},
	}
	free := 30
	for i := 2; i < 361; i++ {
		day := i
		x := float64(day)
		c := DayFeeCalc{
			RoundRideDay: int64(math.Max(float64(i - 1 - free), 2.00)),
			StartRoundDay : "20210821",
		}
		tt := day2sec(c.StartRoundDay) + int64(i) * 86400
		tt_string := time.Unix(tt, 0).Format("20060102")
		c.Time = tt_string
		yy := c.OptimizeDayFee()
		if i - free > 0 {
			sum += yy
		}
		expPointst = append(expPointst, plotter.XY{
			X: x,
			Y: sum,
		})
	}
	fmt.Println(expPointst)
	DataPlot(actPoints, expPointst)
}

// BaicycleFitClassification 分类曲线拟合函数
// 是个样板
func FitClassification() {
	c := [][]string{
		{"1", "10"},
		{"7", "100"},
		{"30", "360"},
		{"90", "630"},
		{"200", "1200"},
		{"365", "1800"},
	} //{"天数",“预估总金额”}
	df := NewDataFrame(c)
	// 数据拟合
	actPoints, expPoints1, fa1, fb1 := OptimizeParabola(&df)

	fmt.Println("Fa1", fa1, "Fb1", fb1)

	_, expPoints2, fa2 := OptimizeSin(&df)

	fmt.Println("Fa2", fa2)

	_, expPoints3, fa3, fb3, fc3 := OptimizeGrade3(&df)

	fmt.Println("Fa3", fa3, "Fb3", fb3, "Fc3", fc3, "Fd3")

	_, expPoints4, fa4 := OptimizeSin(&df)

	fmt.Println("Fa4", fa4)

	// 拟合完成,输出fa,fb

	// 数据绘图

	dataPlot2(actPoints, expPoints1, expPoints2, expPoints3, expPoints4)
	fmt.Println("绘制完成,图形地址: baicycleclassification-fit.png")

	newExpPoints1 := DayFee(expPoints1)

	fmt.Println("newExpPoints1 is:", newExpPoints1)

	newExpPoints2 := DayFee(expPoints2)

	fmt.Println("newExpPoints2 is:", newExpPoints2)

	newExpPoints3 := DayFee(expPoints3)

	fmt.Println("newExpPoints3 is:", newExpPoints3)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值