Last active
April 19, 2017 09:13
-
-
Save agoalofalife/88255db373d6d69ef3b2ac06adcaad86 to your computer and use it in GitHub Desktop.
statistic.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net/http" | |
"sort" | |
"strconv" | |
"strings" | |
"math" | |
) | |
const ( | |
pageTop = `<!DOCTYPE HTML><html><head> | |
<style>.error{color:#FF0000;}</style></head><title>Statistics</title> | |
<body><h3>Statistics</h3> | |
<p>Вычисляет основные статистические данные для заданного списка чисел</p>` | |
form = `<form action="/" method="POST"> | |
<label for="numbers">Numbers (comma or space-separated):</label><br /> | |
<input type="text" name="numbers" size="30"><br /> | |
<input type="submit" value="Calculate"> | |
</form>` | |
pageBottom = `</body></html>` | |
anError = `<p class="error">%s</p>` | |
) | |
type statistics struct { | |
numbers []float64 | |
mean float64 | |
median float64 | |
mode []float64 | |
dev float64 | |
} | |
func main() { | |
http.HandleFunc("/", homePage) | |
if err := http.ListenAndServe(":9001", nil); err != nil { | |
log.Fatal("failed to start server", err) | |
} | |
} | |
func homePage(writer http.ResponseWriter, request *http.Request) { | |
err := request.ParseForm() | |
fmt.Fprint(writer, pageTop, form) | |
if err != nil { | |
fmt.Fprintf(writer, anError, err) | |
} else { | |
if numbers, message, ok := processRequest(request); ok { | |
stats := getStats(numbers) | |
fmt.Fprint(writer, formatStats(stats)) | |
} else if message != "" { | |
fmt.Fprintf(writer, anError, message) | |
} | |
} | |
fmt.Fprint(writer, pageBottom) | |
} | |
func processRequest(request *http.Request) ([]float64, string, bool) { | |
var numbers []float64 | |
if slice, found := request.Form["numbers"]; found && len(slice) > 0 { | |
text := strings.Replace(slice[0], ",", " ", -1) | |
for _, field := range strings.Fields(text) { | |
if x, err := strconv.ParseFloat(field, 64); err != nil { | |
return numbers, "'" + field + "' is invalid", false | |
} else { | |
numbers = append(numbers, x) | |
} | |
} | |
} | |
if len(numbers) == 0 { | |
return numbers, "", false // no data first time form is shown | |
} | |
return numbers, "", true | |
} | |
func getStats(numbers []float64) (stats statistics) { | |
stats.numbers = numbers | |
sort.Float64s(stats.numbers) | |
stats.mean = sum(numbers) / float64(len(numbers)) | |
stats.median = median(numbers) | |
stats.mode = getMode(numbers) | |
stats.dev = dev(numbers) | |
return stats | |
} | |
func sum(numbers []float64) (total float64) { | |
for _, x := range numbers { | |
total += x | |
} | |
return total | |
} | |
func median(numbers []float64) float64 { | |
middle := len(numbers) / 2 | |
result := numbers[middle] | |
if len(numbers)%2 == 0 { | |
result = (result + numbers[middle-1]) / 2 | |
} | |
return result | |
} | |
func formatStats(stats statistics) string { | |
return fmt.Sprintf(`<table border="1"> | |
<tr><th colspan="2">Results</th></tr> | |
<tr><td>Numbers</td><td>%v</td></tr> | |
<tr><td>Count</td><td>%d</td></tr> | |
<tr><td>Mean</td><td>%f</td></tr> | |
<tr><td>Median</td><td>%f</td></tr> | |
<tr><td>Mode</td><td>%v</td></tr> | |
<tr><td>Std Dev.</td><td>%f</td></tr> | |
</table>`, stats.numbers, len(stats.numbers), stats.mean, stats.median, stats.mode, stats.dev ) | |
} | |
func dev(numbers []float64) (dev float64){ | |
var middle float64 | |
for _,val := range numbers { | |
middle += val | |
} | |
middle = middle / float64(len(numbers)) | |
for _,val := range numbers { | |
fmt.Println(math.Pow(val - middle, 2)) | |
dev += math.Pow(val - middle, 2) | |
} | |
dev = dev / float64(len(numbers) - 1) | |
return math.Sqrt(dev) | |
} | |
func getMode( numbers []float64 ) (top []float64) { | |
var state bool | |
m := make(map[float64]float64) | |
numbersOne := make([]float64, len(numbers)) | |
copy(numbersOne, numbers) | |
for _ , oneIterate := range numbersOne{ | |
m[oneIterate], state = countMatches(numbersOne, oneIterate); | |
deleteElement(numbersOne, oneIterate) | |
} | |
var values []float64 | |
for _, count := range m { | |
values = append(values, count) | |
} | |
sort.Sort(sort.Reverse(sort.Float64Slice(values))) | |
//fmt.Println(Ints(values)) | |
if len(values) > 2 && state { | |
top = getKeyOnlyUp( values[0] , m) | |
} | |
return top | |
} | |
func countMatches( where []float64, what float64) (counter float64, err bool) { | |
for _, value := range where{ | |
if what == value{ | |
counter += 1 | |
} | |
} | |
if counter < 1 { | |
return counter, false | |
} | |
return counter, true | |
} | |
func deleteElement( where []float64, what float64) (newArr []float64) { | |
for _, value := range where{ | |
if what != value{ | |
newArr = append(newArr, value) | |
} | |
} | |
return newArr | |
} | |
func getKeyOnlyUp(key float64, hash map[float64]float64) (top []float64){ | |
for value, count := range hash { | |
if count == key { | |
top = append(top, value) | |
} | |
} | |
return top | |
} | |
func Ints(input []float64) []float64 { | |
u := make([]float64, 0, len(input)) | |
m := make(map[float64]bool) | |
for _, val := range input { | |
if _, ok := m[val]; !ok { | |
m[val] = true | |
u = append(u, val) | |
} | |
} | |
return u | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment