Skip to content

Commit

Permalink
- added multi key sorting
Browse files Browse the repository at this point in the history
- added generic series
  • Loading branch information
rocketlaunchr-cto committed Oct 6, 2018
1 parent af545b3 commit 916a14b
Show file tree
Hide file tree
Showing 13 changed files with 1,094 additions and 543 deletions.
30 changes: 10 additions & 20 deletions dataframe.go
Expand Up @@ -98,6 +98,7 @@ func (df *DataFrame) Values(options ...ValuesOptions) func() (*int, map[interfac
}
}

// Prepend inserts a row at the beginning.
func (df *DataFrame) Prepend(vals ...interface{}) {
df.lock.Lock()
defer df.lock.Unlock()
Expand Down Expand Up @@ -135,10 +136,12 @@ func (df *DataFrame) Prepend(vals ...interface{}) {
}
}

// Append inserts a row at the end.
func (df *DataFrame) Append(vals ...interface{}) {
df.Insert(df.n, vals...)
}

// Insert adds a row to a particular position.
func (df *DataFrame) Insert(row int, vals ...interface{}) {
df.lock.Lock()
defer df.lock.Unlock()
Expand Down Expand Up @@ -181,6 +184,7 @@ func (df *DataFrame) insert(row int, vals ...interface{}) {
}
}

// Remove deletes a row.
func (df *DataFrame) Remove(row int) {
df.lock.Lock()
defer df.lock.Unlock()
Expand Down Expand Up @@ -230,6 +234,8 @@ func (df *DataFrame) UpdateRow(row int, vals ...interface{}) {
}
}

// NameToColumn returns the index of the series based on the name.
// The starting index is 0.
func (df *DataFrame) NameToColumn(seriesName string) int {
for idx, aSeries := range df.Series {
if aSeries.Name() == seriesName {
Expand All @@ -244,31 +250,15 @@ func (df *DataFrame) Swap(row1, row2 int) {
df.lock.Lock()
defer df.lock.Unlock()

df.swap(row1, row2)
}

func (df *DataFrame) swap(row1, row2 int) {
for idx := range df.Series {
df.Series[idx].Swap(row1, row2)
}
}

// Sort is used to sort the data according to different keys
func (df *DataFrame) Sort(vals ...interface{}) {
df.lock.Lock()
defer df.lock.Unlock()

// TODO
// if len(vals) > 0 {

// for _, field := range vals {

// series := df.Series[df.NameToColumn(field.(string))]

// series.Slice(series., less func(i, j int) bool)

// }

// }

}

// Lock will lock the dataframe allowing you to directly manipulate
// the underlying series with confidence.
func (df *DataFrame) Lock() {
Expand Down
9 changes: 0 additions & 9 deletions helpers.go
Expand Up @@ -36,12 +36,3 @@ func BoolValueFormatter(v interface{}) string {
return ""
}
}

// DefaultValueFormatter will return a string representation
// of the data in a particular row.
func DefaultValueFormatter(v interface{}) string {
if v == nil {
return "NaN"
}
return fmt.Sprintf("%v", v)
}
17 changes: 7 additions & 10 deletions series.go
Expand Up @@ -67,22 +67,19 @@ type Series interface {
// Sort will sort the series
Sort(options ...Options)

// SortLessFunc is a function that returns true when val1 is
// less than val2. val1 and val2 must not be nil.
// See: https://golang.org/pkg/sort/#SliceStable
SortLessFunc() func(val1 interface{}, val2 interface{}) bool
// IsEqualFunc returns true if a is equal to b
IsEqualFunc(a, b interface{}) bool

// SortEqualFunc is a function that returns true when val1 is
// equal to val2. val1 and val2 must not be nil.
SortEqualFunc() func(val1 interface{}, val2 interface{}) bool
// IsLessThanFunc returns true if a is less than b
IsLessThanFunc(a, b interface{}) bool

// Swap is used to swap 2 values based on their row position.
Swap(row1, row2 int, options ...Options)

// Lock will lock the Series allowing you to directly manipulate
// the underlying slice with confidence.
Lock()

// Unlock will unlock the Series that was previously locked.
Unlock()

// Swap is used to swap 2 values based on their row position.
Swap(row1, row2 int, options ...Options)
}
21 changes: 21 additions & 0 deletions series_defaults.go
@@ -0,0 +1,21 @@
package dataframe

import (
"fmt"

"github.com/google/go-cmp/cmp"
)

// DefaultIsEqualFunc
func DefaultIsEqualFunc(a, b interface{}) bool {
return cmp.Equal(a, b)
}

// DefaultValueFormatter will return a string representation
// of the data in a particular row.
func DefaultValueFormatter(v interface{}) string {
if v == nil {
return "NaN"
}
return fmt.Sprintf("%v", v)
}
246 changes: 246 additions & 0 deletions series_float64.go
@@ -0,0 +1,246 @@
package dataframe

import (
"fmt"
"sort"
"strconv"
"sync"
)

type SeriesFloat64 struct {
valFormatter ValueToStringFormatter

lock sync.RWMutex
name string
Values []*float64
}

// NewSeriesFloat64 creates a new series with the underlying type as float64
func NewSeriesFloat64(name string, init *SeriesInit, vals ...interface{}) *SeriesFloat64 {
s := &SeriesFloat64{
name: name,
Values: []*float64{},
}

var (
size int
capacity int
)

if init != nil {
size = init.Size
capacity = init.Capacity
if size > capacity {
capacity = size
}
}

s.Values = make([]*float64, size, capacity)
s.valFormatter = DefaultValueFormatter

for idx, v := range vals {
if idx < size {
s.Values[idx] = s.valToPointer(v)
} else {
s.Values = append(s.Values, s.valToPointer(v))
}
}

return s
}

func (s *SeriesFloat64) Name() string {
return s.name
}

func (s *SeriesFloat64) Type() string {
return "float64"
}

func (s *SeriesFloat64) NRows(options ...Options) int {
if len(options) > 0 && !options[0].DontLock {
s.lock.RLock()
defer s.lock.RUnlock()
}

return len(s.Values)
}

func (s *SeriesFloat64) Value(row int, options ...Options) interface{} {
if len(options) > 0 && !options[0].DontLock {
s.lock.RLock()
defer s.lock.RUnlock()
}

val := s.Values[row]
if val == nil {
return nil
}
return *val
}

func (s *SeriesFloat64) ValueString(row int, options ...Options) string {
return s.valFormatter(s.Value(row, options...))
}

func (s *SeriesFloat64) Prepend(val interface{}, options ...Options) {
if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}

// See: https://stackoverflow.com/questions/41914386/what-is-the-mechanism-of-using-append-to-prepend-in-go

if cap(s.Values) > len(s.Values) {
// There is already extra capacity so copy current values by 1 spot
s.Values = s.Values[:len(s.Values)+1]
copy(s.Values[1:], s.Values)
s.Values[0] = s.valToPointer(val)
return
}

// No room, new slice needs to be allocated:
s.insert(0, val)
}

func (s *SeriesFloat64) Append(val interface{}, options ...Options) int {
var locked bool
if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
locked = true
}

row := s.NRows(Options{DontLock: locked})
s.insert(row, val)
return row
}

func (s *SeriesFloat64) Insert(row int, val interface{}, options ...Options) {
if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}

s.insert(row, val)
}

func (s *SeriesFloat64) insert(row int, val interface{}) {
s.Values = append(s.Values, nil)
copy(s.Values[row+1:], s.Values[row:])
s.Values[row] = s.valToPointer(val)
}

func (s *SeriesFloat64) Remove(row int, options ...Options) {
if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}

s.Values = append(s.Values[:row], s.Values[row+1:]...)
}

func (s *SeriesFloat64) Update(row int, val interface{}, options ...Options) {
if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}

s.Values[row] = s.valToPointer(val)
}

func (s *SeriesFloat64) valToPointer(v interface{}) *float64 {
if v == nil {
return nil
} else {
f, err := strconv.ParseFloat(fmt.Sprintf("%v", v), 64)
if err != nil {
_ = v.(float64)
}
return &f
}
}

func (s *SeriesFloat64) SetValueToStringFormatter(f ValueToStringFormatter) {
if f == nil {
s.valFormatter = DefaultValueFormatter
return
}
s.valFormatter = f
}

func (s *SeriesFloat64) Swap(row1, row2 int, options ...Options) {
if row1 == row2 {
return
}

if len(options) > 0 && !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}

s.Values[row1], s.Values[row2] = s.Values[row2], s.Values[row1]
}

func (s *SeriesFloat64) IsEqualFunc(a, b interface{}) bool {
f1 := a.(float64)
f2 := b.(float64)

return f1 == f2
}

func (s *SeriesFloat64) IsLessThanFunc(a, b interface{}) bool {
f1 := a.(float64)
f2 := b.(float64)

return f1 < f2
}

func (s *SeriesFloat64) Sort(options ...Options) {

var sortDesc bool

if len(options) > 0 {
if !options[0].DontLock {
s.lock.Lock()
defer s.lock.Unlock()
}
sortDesc = options[0].SortDesc
}

sort.SliceStable(s.Values, func(i, j int) (ret bool) {
defer func() {
if sortDesc {
ret = !ret
}
}()

if s.Values[i] == nil {
if s.Values[j] == nil {
// both are nil
return true
} else {
return true
}
} else {
if s.Values[j] == nil {
// i has value and j is nil
return false
} else {
// Both are not nil
ti := *s.Values[i]
tj := *s.Values[j]

return ti < tj
}
}
})
}

func (s *SeriesFloat64) Lock() {
s.lock.Lock()
}

func (s *SeriesFloat64) Unlock() {
s.lock.Unlock()
}

0 comments on commit 916a14b

Please sign in to comment.