From f920006fe2148d29c8af7cbd711227b2b88acf42 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 06:35:25 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=8A=A0=E8=BD=BDMap=E7=9A=84=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_map.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 dataframe_map.go diff --git a/dataframe_map.go b/dataframe_map.go new file mode 100644 index 0000000..ca19279 --- /dev/null +++ b/dataframe_map.go @@ -0,0 +1,47 @@ +package pandas + +import ( + "fmt" + "sort" +) + +// LoadMaps creates a new DataFrame based on the given maps. This function assumes +// that every map on the array represents a row of observations. +func LoadMaps(maps []map[string]interface{}, options ...LoadOption) DataFrame { + if len(maps) == 0 { + return DataFrame{Err: fmt.Errorf("load maps: empty array")} + } + inStrSlice := func(i string, s []string) bool { + for _, v := range s { + if v == i { + return true + } + } + return false + } + // Detect all colnames + var colnames []string + for _, v := range maps { + for k := range v { + if exists := inStrSlice(k, colnames); !exists { + colnames = append(colnames, k) + } + } + } + sort.Strings(colnames) + records := make([][]string, len(maps)+1) + records[0] = colnames + for k, m := range maps { + row := make([]string, len(colnames)) + for i, colname := range colnames { + element := "" + val, ok := m[colname] + if ok { + element = fmt.Sprint(val) + } + row[i] = element + } + records[k+1] = row + } + return LoadRecords(records, options...) +} -- Gitee From 48d5b40c8e2d7e8a17f6606fbbe290813911d290 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 06:42:37 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E4=BB=8Egonum=20mat.Matrix=E7=9A=84=E5=8A=A0=E8=BD=BD=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9A=84=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_matrix.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 dataframe_matrix.go diff --git a/dataframe_matrix.go b/dataframe_matrix.go new file mode 100644 index 0000000..58a06f0 --- /dev/null +++ b/dataframe_matrix.go @@ -0,0 +1,32 @@ +package pandas + +import "gonum.org/v1/gonum/mat" + +// LoadMatrix loads the given Matrix as a DataFrame +// TODO: Add Loadoptions +func LoadMatrix(mat mat.Matrix) DataFrame { + nrows, ncols := mat.Dims() + columns := make([]Series, ncols) + for i := 0; i < ncols; i++ { + floats := make([]float64, nrows) + for j := 0; j < nrows; j++ { + floats[j] = mat.At(j, i) + } + columns[i] = NewSeries(SERIES_TYPE_FLOAT, "", floats) + } + nrows, ncols, err := checkColumnsDimensions(columns...) + if err != nil { + return DataFrame{Err: err} + } + df := DataFrame{ + columns: columns, + ncols: ncols, + nrows: nrows, + } + colnames := df.Names() + fixColnames(colnames) + for i, colname := range colnames { + df.columns[i].Rename(colname) + } + return df +} -- Gitee From a95bf6d905593d33ad9c5ce074b2d2e459b6165a Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 06:47:08 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E6=8B=86=E5=88=86options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe.go | 94 ------------------------------------------- dataframe_options.go | 95 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 94 deletions(-) create mode 100644 dataframe_options.go diff --git a/dataframe.go b/dataframe.go index 784b507..3edba58 100644 --- a/dataframe.go +++ b/dataframe.go @@ -229,100 +229,6 @@ func findInStringSlice(str string, s []string) int { // LoadOption is the type used to configure the load of elements type LoadOption func(*loadOptions) -type loadOptions struct { - // Specifies which is the default type in case detectTypes is disabled. - defaultType Type - - // If set, the type of each column will be automatically detected unless - // otherwise specified. - detectTypes bool - - // If set, the first row of the tabular structure will be used as column - // names. - hasHeader bool - - // The names to set as columns names. - names []string - - // Defines which values are going to be considered as NaN when parsing from string. - nanValues []string - - // Defines the csv delimiter - delimiter rune - - // EnablesLazyQuotes - lazyQuotes bool - - // Defines the comment delimiter - comment rune - - // The types of specific columns can be specified via column name. - types map[string]Type -} - -// DefaultType sets the defaultType option for loadOptions. -func DefaultType(t Type) LoadOption { - return func(c *loadOptions) { - c.defaultType = t - } -} - -// DetectTypes sets the detectTypes option for loadOptions. -func DetectTypes(b bool) LoadOption { - return func(c *loadOptions) { - c.detectTypes = b - } -} - -// HasHeader sets the hasHeader option for loadOptions. -func HasHeader(b bool) LoadOption { - return func(c *loadOptions) { - c.hasHeader = b - } -} - -// Names sets the names option for loadOptions. -func Names(names ...string) LoadOption { - return func(c *loadOptions) { - c.names = names - } -} - -// NaNValues sets the nanValues option for loadOptions. -func NaNValues(nanValues []string) LoadOption { - return func(c *loadOptions) { - c.nanValues = nanValues - } -} - -// WithTypes sets the types option for loadOptions. -func WithTypes(coltypes map[string]Type) LoadOption { - return func(c *loadOptions) { - c.types = coltypes - } -} - -// WithDelimiter sets the csv delimiter other than ',', for example '\t' -func WithDelimiter(b rune) LoadOption { - return func(c *loadOptions) { - c.delimiter = b - } -} - -// WithLazyQuotes sets csv parsing option to LazyQuotes -func WithLazyQuotes(b bool) LoadOption { - return func(c *loadOptions) { - c.lazyQuotes = b - } -} - -// WithComments sets the csv comment line detect to remove lines -func WithComments(b rune) LoadOption { - return func(c *loadOptions) { - c.comment = b - } -} - func parseType(s string) (Type, error) { switch s { case "float", "float64", "float32": diff --git a/dataframe_options.go b/dataframe_options.go new file mode 100644 index 0000000..d18d65a --- /dev/null +++ b/dataframe_options.go @@ -0,0 +1,95 @@ +package pandas + +type loadOptions struct { + // Specifies which is the default type in case detectTypes is disabled. + defaultType Type + + // If set, the type of each column will be automatically detected unless + // otherwise specified. + detectTypes bool + + // If set, the first row of the tabular structure will be used as column + // names. + hasHeader bool + + // The names to set as columns names. + names []string + + // Defines which values are going to be considered as NaN when parsing from string. + nanValues []string + + // Defines the csv delimiter + delimiter rune + + // EnablesLazyQuotes + lazyQuotes bool + + // Defines the comment delimiter + comment rune + + // The types of specific columns can be specified via column name. + types map[string]Type +} + +// DefaultType sets the defaultType option for loadOptions. +func DefaultType(t Type) LoadOption { + return func(c *loadOptions) { + c.defaultType = t + } +} + +// DetectTypes sets the detectTypes option for loadOptions. +func DetectTypes(b bool) LoadOption { + return func(c *loadOptions) { + c.detectTypes = b + } +} + +// HasHeader sets the hasHeader option for loadOptions. +func HasHeader(b bool) LoadOption { + return func(c *loadOptions) { + c.hasHeader = b + } +} + +// Names sets the names option for loadOptions. +func Names(names ...string) LoadOption { + return func(c *loadOptions) { + c.names = names + } +} + +// NaNValues sets the nanValues option for loadOptions. +func NaNValues(nanValues []string) LoadOption { + return func(c *loadOptions) { + c.nanValues = nanValues + } +} + +// WithTypes sets the types option for loadOptions. +func WithTypes(coltypes map[string]Type) LoadOption { + return func(c *loadOptions) { + c.types = coltypes + } +} + +// WithDelimiter sets the csv delimiter other than ',', for example '\t' +func WithDelimiter(b rune) LoadOption { + return func(c *loadOptions) { + c.delimiter = b + } +} + +// WithLazyQuotes sets csv parsing option to LazyQuotes +func WithLazyQuotes(b bool) LoadOption { + return func(c *loadOptions) { + c.lazyQuotes = b + } +} + +// WithComments sets the csv comment line detect to remove lines +func WithComments(b rune) LoadOption { + return func(c *loadOptions) { + c.comment = b + } +} -- Gitee From 39d336b9380caaf41f5345857413f9cdd60881d0 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 07:55:23 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E6=8B=86=E5=88=86options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_options.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dataframe_options.go b/dataframe_options.go index d18d65a..0d2e6a3 100644 --- a/dataframe_options.go +++ b/dataframe_options.go @@ -93,3 +93,18 @@ func WithComments(b rune) LoadOption { c.comment = b } } + +// WriteOption is the type used to configure the writing of elements +type WriteOption func(*writeOptions) + +type writeOptions struct { + // Specifies whether the header is also written + writeHeader bool +} + +// WriteHeader sets the writeHeader option for writeOptions. +func WriteHeader(b bool) WriteOption { + return func(c *writeOptions) { + c.writeHeader = b + } +} -- Gitee From 1f1b8ed1365e07c94d5fc5171791ba972440f5e3 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 07:56:58 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E6=8B=86=E5=88=86options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_csv.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/dataframe_csv.go b/dataframe_csv.go index e8bc327..833dc73 100644 --- a/dataframe_csv.go +++ b/dataframe_csv.go @@ -60,21 +60,6 @@ func ReadCSV(in any, options ...LoadOption) DataFrame { return LoadRecords(records, options...) } -// WriteOption is the type used to configure the writing of elements -type WriteOption func(*writeOptions) - -type writeOptions struct { - // Specifies whether the header is also written - writeHeader bool -} - -// WriteHeader sets the writeHeader option for writeOptions. -func WriteHeader(b bool) WriteOption { - return func(c *writeOptions) { - c.writeHeader = b - } -} - // WriteCSV writes the DataFrame to the given io.Writer as a CSV file. // 支持文件名和io两种方式写入数据 func (self DataFrame) WriteCSV(out any, options ...WriteOption) error { -- Gitee From 04b830c1881a1730f761fc46055c50debf168ac4 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:06:28 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=BA=9F=E5=BC=83?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_csv.go | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/dataframe_csv.go b/dataframe_csv.go index 833dc73..8d9efd7 100644 --- a/dataframe_csv.go +++ b/dataframe_csv.go @@ -60,9 +60,8 @@ func ReadCSV(in any, options ...LoadOption) DataFrame { return LoadRecords(records, options...) } -// WriteCSV writes the DataFrame to the given io.Writer as a CSV file. -// 支持文件名和io两种方式写入数据 -func (self DataFrame) WriteCSV(out any, options ...WriteOption) error { +// WriteExcel 支持文件名和io两种方式写入数据 +func (self DataFrame) WriteExcel(out any, options ...WriteOption) error { var ( writer io.Writer filename string @@ -103,18 +102,3 @@ func (self DataFrame) WriteCSV(out any, options ...WriteOption) error { return csv.NewWriter(writer).WriteAll(records) } - -// ToCSV 写csv格式文件 -func (self DataFrame) oldToCSV(filename string, options ...WriteOption) error { - filepath, err := homedir.Expand(filename) - if err != nil { - return err - } - csvFile, err := os.Create(filepath) - if err != nil { - return err - } - defer api.CloseQuietly(csvFile) - err = self.WriteCSV(csvFile, options...) - return err -} -- Gitee From 5d2db7ab14036d7914902438e8e80f52bc089154 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:52:26 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E7=BA=A6=E5=AE=9A=E5=8F=AA=E5=BF=BD?= =?UTF-8?q?=E7=95=A5test-*-w*=E6=A0=BC=E5=BC=8F=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8D,=20=E8=BF=99=E9=83=A8=E5=88=86=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=98=AF=E6=B5=8B=E8=AF=95=E4=B8=AD=E8=BE=93=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index da13d98..040c7f7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ assembly/version.properties coverage.txt # test -tutorials.csv +test-*-w* -- Gitee From 36c78f6af623eaa3b8c74a6543868147ecfe7d59 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:53:06 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=86=99csv=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E6=96=87=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/series1_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/series1_test.go b/tests/series1_test.go index 61494a5..6c045f0 100644 --- a/tests/series1_test.go +++ b/tests/series1_test.go @@ -23,7 +23,7 @@ Spain,2012-02-01,66,555.42,00241 ` df := pandas.ReadCSV(strings.NewReader(csvStr)) fmt.Println(df) - filename := "tutorials.csv" + filename := "test-tutorials-w01.csv" _ = df.WriteCSV(filename) buf := new(bytes.Buffer) _ = df.WriteCSV(buf) -- Gitee From b238c70b54f170cb673cb3a2fdeaff0499af3e64 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:53:29 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95excel?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testfiles/test-excel-r01.xlsx | Bin 0 -> 41977 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testfiles/test-excel-r01.xlsx diff --git a/testfiles/test-excel-r01.xlsx b/testfiles/test-excel-r01.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..fc4298e87d21d4f8b801874bb8cde78b987fe87c GIT binary patch literal 41977 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 literal 0 HcmV?d00001 -- Gitee From 2b11ed88e2c8a783c9a5e5fadee62d04e37d6e61 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:53:59 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E9=A2=84=E7=95=99quant1x=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builtin.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin.go b/builtin.go index 94d8232..342d9ef 100644 --- a/builtin.go +++ b/builtin.go @@ -8,6 +8,10 @@ import ( ) // 收敛统一初始化 +const ( + quant1xPath = "~/.quant1x" // quant1x默认 + tmpDir = quant1xPath + "/tmp" // 临时路径 +) // 全局变量定义 -- Gitee From c9b08b5ac89bf0168ced50a92cbee4b253ecd08c Mon Sep 17 00:00:00 2001 From: wangfeng Date: Sun, 5 Feb 2023 08:55:20 +0800 Subject: [PATCH 11/11] =?UTF-8?q?#I6C2TX=20=E8=AF=BB=E5=86=99excel,=20?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E5=92=8C=E4=BB=A3=E7=A0=81=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E6=A0=BC=E5=BC=8F=E4=B8=8D=E5=A4=AA=E5=87=86=E7=A1=AE?= =?UTF-8?q?,=20=E7=95=99=E5=88=B0=E4=B8=8B=E6=9C=9F=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataframe_csv.go | 5 +-- dataframe_excel.go | 78 +++++++++++++++++++++++++++++++++++++++++ dataframe_excel_test.go | 17 +++++++++ go.mod | 1 + go.sum | 6 ++++ 5 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 dataframe_excel.go create mode 100644 dataframe_excel_test.go diff --git a/dataframe_csv.go b/dataframe_csv.go index 8d9efd7..19a7a6f 100644 --- a/dataframe_csv.go +++ b/dataframe_csv.go @@ -60,8 +60,9 @@ func ReadCSV(in any, options ...LoadOption) DataFrame { return LoadRecords(records, options...) } -// WriteExcel 支持文件名和io两种方式写入数据 -func (self DataFrame) WriteExcel(out any, options ...WriteOption) error { +// WriteCSV writes the DataFrame to the given io.Writer as a CSV file. +// 支持文件名和io两种方式写入数据 +func (self DataFrame) WriteCSV(out any, options ...WriteOption) error { var ( writer io.Writer filename string diff --git a/dataframe_excel.go b/dataframe_excel.go new file mode 100644 index 0000000..33a86f0 --- /dev/null +++ b/dataframe_excel.go @@ -0,0 +1,78 @@ +package pandas + +import ( + "fmt" + "github.com/mymmsc/gox/logger" + "github.com/mymmsc/gox/util/homedir" + "github.com/tealeg/xlsx" +) + +// 读取excel文件 +func ReadExcel(filename string, options ...LoadOption) DataFrame { + if IsEmpty(filename) { + return DataFrame{Err: fmt.Errorf("filaname is empty")} + } + + filepath, err := homedir.Expand(filename) + if err != nil { + logger.Errorf("%s, error=%+v\n", filename, err) + return DataFrame{Err: err} + } + //filename := "test.xlsx" + xlFile, err := xlsx.OpenFile(filepath) + if err != nil { + return DataFrame{Err: err} + } + colnums := make([][]string, 0) + for _, sheet := range xlFile.Sheets { + //fmt.Printf("Sheet Name: %s\n", sheet.Name) + for _, row := range sheet.Rows { + col := make([]string, 0) + for _, cell := range row.Cells { + text := cell.String() + col = append(col, text) + } + colnums = append(colnums, col) + } + // 只展示第一个sheet + break + } + + return LoadRecords(colnums, options...) +} + +// WriteExcel 支持文件名和io两种方式写入数据 +func (self DataFrame) WriteExcel(filename string, options ...WriteOption) error { + filepath, err := homedir.Expand(filename) + if err != nil { + return err + } + xlFile := xlsx.NewFile() + sheet, err := xlFile.AddSheet("Sheet(pandas)") + if err != nil { + return err + } + // Set the default write options + cfg := writeOptions{ + writeHeader: true, + } + + // Set any custom write options + for _, option := range options { + option(&cfg) + } + + records := self.Records() + if !cfg.writeHeader { + records = records[1:] + } + for _, cols := range records { + row := sheet.AddRow() + for _, col := range cols { + cell := row.AddCell() + cell.SetString(col) + } + } + + return xlFile.Save(filepath) +} diff --git a/dataframe_excel_test.go b/dataframe_excel_test.go new file mode 100644 index 0000000..2862e39 --- /dev/null +++ b/dataframe_excel_test.go @@ -0,0 +1,17 @@ +package pandas + +import ( + "fmt" + "testing" +) + +func TestReadExcel(t *testing.T) { + filename := "./testfiles/test-excel-r01.xlsx" + df := ReadExcel(filename) + fmt.Println(df) + toFile := "./testfiles/test-excel-w01.xlsx" + err := df.WriteExcel(toFile) + if err != nil { + t.Errorf("write excel=%s, failed", toFile) + } +} diff --git a/go.mod b/go.mod index cf53890..61ff168 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/google/go-cmp v0.5.8 github.com/huandu/go-clone v1.4.1 github.com/mymmsc/gox v1.3.1 + github.com/tealeg/xlsx v1.0.5 github.com/viterin/vek v0.4.0 gonum.org/v1/gonum v0.12.0 ) diff --git a/go.sum b/go.sum index 05d0989..0d0b4f6 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,11 @@ github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3 github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= github.com/huandu/go-clone v1.4.1 h1:QQYjiLadyxOvdwgZoH8f1xGkvvf4+Cm8be7fo9W2QQA= github.com/huandu/go-clone v1.4.1/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -22,6 +25,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= +github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= github.com/viterin/partial v1.0.0 h1:e6z0cWJ+SddpXHoLU4ikIDrsI/ZE+p+hqMsB++8IfwE= github.com/viterin/partial v1.0.0/go.mod h1:K9y+kVePpmfZN510YNHoUs+6scZ2K7BLojfI8aW2nw0= github.com/viterin/vek v0.4.0 h1:P34BWVGd3pSZFma9SE+G1pTucMGtw9p79I+Hull/+Ao= @@ -33,6 +38,7 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -- Gitee