Related
I am looking for a way to shade an area to the left of cursor to visually see the proportion of total area under the density curve where x < X, i.e. a visual feel for a cumulative density function. Is there an easy/elegant way to do this in highcharts?
library(highcharter)
df <- data.frame(x = seq(-3, 3, 0.02),
pdf = dnorm(seq(-3, 3, 0.02)),
cdf = pnorm(seq(-3, 3, 0.02)))
highchart() |>
hc_add_series(df, "line", hcaes(x, pdf)) |>
hc_tooltip(
headerFormat = "",
pointFormat = "{point.cdf}"
) |>
hc_xAxis(crosshair = T)
You can try changing the shading parameters in the SVG attributes, these options are only available from the wrap core Highcharts code.
(function(H) {
H.wrap(H.Axis.prototype, 'drawCrosshair', function(p, e, point) {
p.call(this, e, point)
console.log(this, e, point);
if (this.cross && point) {
this.cross.attr({
stroke: point.series.color,
d: "M 10 50 L 200 50"
})
}
})
}(Highcharts));
Highcharts.chart('container', {
xAxis: {
crosshair: true
},
yAxis: {
crosshair: true
},
series: [{
data: [20, 30, 50]
}, {
data: [10, 20, 15]
}]
});
Demo: http://jsfiddle.net/BlackLabel/xe902c6q/
What would be the best way to sort a Map where keys are Strings and values are Lists of ints.
var userVotes = {
"arbourn": [1, 0, 7],
"burun": [2, 9, 0, 1],
"niko": [1, 0, 3, 10],
};
The sorting is done on the basis of Lists (values).
1. List with first value higher wins
2. In case of same values, List with bigger length wins.
3. Else, no change in map (not very important)
Expected output is
var userVotes = {
"burun": [2, 9, 0, 1],
"arbourn": [1, 0, 7],
"niko": [1, 0, 3, 10],
};
Performance Criteria - The length of List is bounded to be <=100 and number of keys <=1000.
Thanks!
Something like this could be done:
import 'dart:collection';
void main() {
final userVotes = {
"arbourn": [1, 0, 7],
"burun": [2, 9, 0, 1],
"niko": [1, 0, 3, 10],
"niko2": [1, 0, 3, 10, 0],
};
final sortedMap =
LinkedHashMap.fromEntries(userVotes.entries.toList()..sort(sortMethod));
sortedMap.forEach((key, value) => print('$key: $value'));
}
int sortMethod(MapEntry<String, List<int>> e1, MapEntry<String, List<int>> e2) {
final l1 = e1.value;
final l2 = e2.value;
final minLength = l1.length > l2.length ? l2.length : l1.length;
for (var i = 0; i < minLength; i++) {
if (l1[i] > l2[i]) {
return -1;
} else if (l1[i] < l2[i]) {
return 1;
}
}
return l2.length.compareTo(l1.length);
}
Which outputs:
burun: [2, 9, 0, 1]
arbourn: [1, 0, 7]
niko2: [1, 0, 3, 10, 0]
niko: [1, 0, 3, 10]
Edit
Since it is not really that great to sort maps, a better strategy would be to convert each key-value pair into a UserVote object which implements compareTo to other UserVote objects. This objects can be put into a list and sorted.
import 'dart:collection';
class UserVote implements Comparable<UserVote> {
final String name;
final List<int> list;
const UserVote(this.name, this.list);
#override
int compareTo(UserVote o) {
final minLength = list.length > o.list.length ? o.list.length : list.length;
for (var i = 0; i < minLength; i++) {
if (list[i] > o.list[i]) {
return -1;
} else if (list[i] < o.list[i]) {
return 1;
}
}
return o.list.length.compareTo(list.length);
}
#override
String toString() => '$name: $list';
}
void main() {
final userVotes = {
"arbourn": [1, 0, 7],
"burun": [2, 9, 0, 1],
"niko": [1, 0, 3, 10],
"niko2": [1, 0, 3, 10, 0],
};
final listOfUserVotes =
userVotes.entries.map((e) => UserVote(e.key, e.value)).toList();
listOfUserVotes.sort();
listOfUserVotes.forEach(print);
}
Is it possible to have both colorAxis and series in the legend? http://jsfiddle.net/6k17dojn/ i see i can only show one at a time when I toggle this setting
colorAxis: {
showInLegend: true,
}
Currently to show a basic legend with colorAxis, you need to add some code to Highcharts core. This plugin below allows you to add colorAxis to a legend if showInLegend property is set to false:
(function(H) {
H.addEvent(H.Legend, 'afterGetAllItems', function(e) {
var colorAxisItems = [],
colorAxis = this.chart.colorAxis[0],
i;
if (colorAxis && colorAxis.options) {
if (colorAxis.options.dataClasses) {
colorAxisItems = colorAxis.getDataClassLegendSymbols();
} else {
colorAxisItems.push(colorAxis);
}
}
i = colorAxisItems.length;
while (i--) {
e.allItems.unshift(colorAxisItems[i]);
}
});
}(Highcharts))
Live demo: http://jsfiddle.net/BlackLabel/hs1zeruy/
API Reference: https://api.highcharts.com/highcharts/colorAxis.showInLegend
Docs: https://www.highcharts.com/docs/extending-highcharts
It's possible, but not with the data you currently work with. A heatmap's data is a set of coordinates, but here, your two series overlap.
Your raw data is :
[
[0,0,0.2, 0.4],
[0,1,0.1, 0.5],
[0,2,0.4, 0.9],
[0,3,0.7, 0.1],
[0,4,0.3, 0.6]
]
From there, you're mapping two series: 2018, and 2019 via the seriesMapping: [{x: 0, y: 1, value: 2}, {x: 0, y: 1, value: 3}] option.
You thus end up with the following two series:
2018 2019 2019 should be
[ [ [
[0, 0, 0.2], [0, 0, 0.4], [1, 0, 0.4],
[0, 1, 0.1], [0, 1, 0.5], [1, 1, 0.5],
[0, 2, 0.4], [0, 2, 0.9], [1, 2, 0.9],
[0, 3, 0.7], [0, 3, 0.1], [1, 3, 0.1],
[0, 4, 0.3] [0, 4, 0.6] [1, 4, 0.6]
] ] ]
Notice that in both cases, the coordinates are the same, but for 2019, the x value should be 1. Since you have 0 as x coordinate for both series, they overlap.
To fix you issue, you need to change your data (or pre-process it, whatever is easier). For example:
var data = '[[0,0,0.2, 0.4],[0,1,0.1, 0.5],[0,2,0.4, 0.9],[0,3,0.7, 0.1],[0,4,0.3, 0.6]]';
var rows = JSON.parse(data);
rows = $.map(rows, function(arr){
return [[
arr[0], arr[1], arr[2], // 2018
arr[0] + 1, arr[1], arr[3], // 2019
]];
});
// and the seriesMapping changes to
data: {
rows: rows,
firstRowAsNames: false,
seriesMapping: [{x: 0, y: 1, value: 2}, {x: 3, y: 4, value: 5}]
},
You can see it in action here: http://jsfiddle.net/Metoule/qgd2ca6p/6/
I need to sort an array of elements based on their frequency, for example:
Input array: [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
Expected output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]
I tried with the code below:
var set: NSCountedSet = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var dictionary = [Int: Int]()
set.forEach { (item) in
dictionary[item as! Int] = set.count(for: item)
}
dictionary.keys.sorted()
print(dictionary)
Description: As 1, 3, 4 occur only once, they are shown at the beginning, 2 occurs two times, 5 three times, 6 four times. And [1, 3, 4] are sorted among them.
Expected result: Time complexity should be O(n)
You can achieve the results in O(nlogn) time by first creating a Dictionary containing the number of occurrences for each element (O(n)), then calling sorted on the Array (Swift uses Introsort, which is O(nlogn)) and using the values from the previously created Dictionary for the sorting. The elements of your array need to be Comparable for sorting to work and Hashable to be able to store them in a Dictionary, which provides O(1) element lookup.
extension Array where Element: Comparable & Hashable {
func sortByNumberOfOccurences() -> [Element] {
let occurencesDict = self.reduce(into: [Element:Int](), { currentResult, element in
currentResult[element, default: 0] += 1
})
return self.sorted(by: { current, next in occurencesDict[current]! < occurencesDict[next]!})
}
}
[1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2].sortByNumberOfOccurences() // [1, 4, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]
The above solution preserves the order of elements that occur an equal number of times. If you actually want to sort such elements based on their compared values (which is what your sample output does), you can modify the closure in sorted like below:
return self.sorted(by: {occurencesDict[$0]! <= occurencesDict[$1]! && $0 < $1})
Or even shorter, comparing tuples for sorting:
return self.sorted(by: {(occurencesDict[$0]!,$0) < (occurencesDict[$1]!,$1)})
which produces the sample output you provided, [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]
You can try
let dd = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
let res = dd.sorted { f, s in
dd.filter { $0 == f }.count < dd.filter { $0 == s }.count
}
print(res) // [1, 4, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]
There is no way to sort with O(n) time complexity. Look at the worst case complexity for popular algorithms at Wikipedia.
The better worst-case time complexity is O(nlogn). Here is how we can solve it with O(nlogn) time complexity:
let array = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
extension Array where Element: Comparable & Hashable {
func countableSorted() -> [Element] {
var counts = [Element: Int]()
// O(n)
for element in self {
counts[element] = (counts[element] ?? 0) + 1
}
// I think that standart method uses O(nlogn) time complexity.
// O(nlogn) + O(n) approximately equal to O(nlogn).
let sorted = counts.sorted { item1, item2 -> Bool in
if item2.value > item1.value {
return true
}
if item2.value == item1.value {
return item2.key > item1.key
}
return false
}
var result = [Element]()
// O(n)
for item in sorted {
let items = Array(repeating: item.key, count: item.value)
result.append(contentsOf: items)
}
// Total time complexity for worst case scenario is O(nlogn)
return result
}
}
print(array.countableSorted())
// Output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]
You can try the below code, this worked properly.
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
inputArray.sort()
let freq = inputArray.sorted { f, s in
inputArray.filter { $0 == f}.count < inputArray.filter { $0 == s}.count
}
print(freq)
Not sure about the time complexity.
I want to add a solution in O(n)
Sorting takes O(nLogn) but this question can also be solved without using sorting by help of HashMap in Java because it contains the pairs sorted in accordance to the key.
import java.util.*;
class Simple
{
public static void main(String[] arg)
{ int inputArray[] = {1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2};
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
Map<Integer,List<Integer>> map2 = new HashMap<Integer,List<Integer>>();
for(int i: inputArray)
{
if(map.get(i) == null){
map.put(i, 1) ;
}
else{
int a = map.get(i);
map.put(i,a+1);
}
}
// using for-each loop for iteration over Map.entrySet()
for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
if(map2.get(entry.getValue()) == null){
map2.put(entry.getValue(), new ArrayList<Integer>()) ;
}
map2.get(entry.getValue()).add(entry.getKey());
}
for(Map.Entry<Integer,List<Integer>> entry : map2.entrySet()){
for(int j=0; j<entry.getValue().size(); j++){
for(int i=0; i<entry.getKey(); i++){
System.out.print(entry.getValue().get(j) + " ");
}
}
}
}
}
In First for loop I am iterating through array saving pair of (value,Occurrence) in map1(HashMap). This will take O(n) as HashMap put operation(insertion) takes O(1).
In second for loop I am iterating map1 and inserting pair of (occurrence, list of numbers in the given array with that occurrence) in map2(HashMap2).
Now in last for loop I am iterating through map2 and printing all the lists one by one it means I am printing each element of given array once i.e. I am iterating through the list of each key and printing each element of the list key number of times. So this would also take O(n).
more about HashMap
Time Complexity : O(n)
Swift Version of above code
extension Array where Element: Comparable & Hashable {
func sortByNumberOfOccurences() -> [Element] {
let occurencesDict = self.reduce(into: [Element:Int](), { currentResult, element in
currentResult[element, default: 0] += 1
})
let dict = occurencesDict.sorted(by: {$0.0 < $1.0})
var dictioanary = [Int:Array]()
for (element,occurence) in dict {
if dictioanary[occurence] == nil
{
dictioanary[occurence] = Array()
}
dictioanary[occurence]?.append(element)
}
var resultArray = Array()
let finalDict = dictioanary.sorted(by: {$0.0 < $1.0})
for (frequency,allValuesOccuringWithThisFrequncy) in finalDict {
for i in allValuesOccuringWithThisFrequncy
{
var j = 0
while(j < frequency)
{
resultArray.append(i)
j = j + 1
}
}
}
print(resultArray)
return resultArray
}
}
Time Complexity in Swift O(nLogn)
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var map:[Int: Int] = [:]
for element in inputArray {
let count = map[element]
if count == nil {
map[element] = 1
} else {
map[element] = count! + 1
}
}
var keysArray = map.keys
let sortedKeys = keysArray.sorted { (number1, number2) -> Bool in
if map[number1]! == map[number2]! {
return number1 < number2
} else {
return map[number1]! < map[number2]!
}
}
var finalArray: [Int] = []
for element in sortedKeys {
for _ in 1...map[element]! {
finalArray.append(element)
}
}
print(finalArray)
Time Complexity: O(nlogn)
Try this solution. It worked for me like a charm :)
func numberOfOccurences(in array: [Int], of element: Int) -> Int {
let object = NSCountedSet(array: array)
return object.count(for: element)
}
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var uniqueElements = Array(Set(inputArray))
var otherArray: [Int] = []
var duplicateElements = uniqueElements.filter { (element) -> Bool in
return (inputArray.contains(element) && numberOfOccurences(in: inputArray, of: element) > 1)
}
uniqueElements = uniqueElements.filter({ !duplicateElements.contains($0) }).sorted()
for item in duplicateElements {
let occurences = numberOfOccurences(in: inputArray, of: item)
for _ in 0...occurences - 1 {
otherArray.append(item)
}
}
otherArray = otherArray.sorted()
duplicateElements.removeAll()
let mergedArray = uniqueElements + otherArray
print(mergedArray)
I think this kind of sorting can be achieved in O(n), with something like the following:
let input = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
// build the frequency dictionary (easy task)
let frequencies = input.reduce(into: [:]) { $0[$1] = ($0[$1] ?? 0) + 1 }
// allocate a 2-D array of ints, each item in this array will hold the numbers that
// appear I times in the input array
let frequencyTable: [[Int]] = frequencies.reduce(into: Array(repeating: [Int](), count: input.count)) {
// substracting one as arrays are zero indexed
// we can't get of of bounds here since the maximum frequency is the
// number of elements in the input array
// Also replicating the numbers by their frequency, to have
// the same contents as in the input array
$0[$1.value - 1] += Array(repeating: $1.key, count: $1.value)
}
// lastly, simply flatten the 2-D array to get the output we need
let output = frequencyTable.flatMap { $0 }
print(output)
Sample result:
[4, 1, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]
Note that the order of numbers with the same frequency might differ based on how the dictionary hash function works.
Also we sacrifice space (allocated 2-D array) in favour of time.
The frequencyTable contents will look similar to this (again the order of 1, 4, 3 might differ):
[[4, 3, 1], [2, 2], [5, 5, 5], [6, 6, 6, 6], [], [], [], [], [], [], [], []]
I have this chart
<script src="http://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 300px"></div>
var seriesOptions = [],
seriesCounter = 0,
names = ['MSFT', 'AAPL', 'GOOG'];
/**
* Create the chart when all data is loaded
* #returns {undefined}
*/
function createChart() {
Highcharts.stockChart('container', {
plotOptions: {
series: {
gapSize: 5 * 24 * 3600 * 1000,
gapUnit: 'relative'
}
},
rangeSelector: {
selected: 5
},
yAxis: {
labels: {
formatter: function () {
return (this.value > 0 ? ' + ' : '') + this.value + '%';
}
},
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
plotOptions: {
series: {
compare: 'percent',
showInNavigator: true
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2,
split: true
},
series: seriesOptions
});
}
$.each(names, function (i, name) {
$.getJSON('https://www.highcharts.com/samples/data/' + name.toLowerCase() + '-c.json', function (data) {
if (i==0) {
var first = [], last = [];
first.push.apply(first, data.slice(0,1)[0]);
last.push.apply(first, data.slice(0,1)[0]);
first[0] = first[0] - 1900 * 24 * 3600 * 1000;
last[0] = last[0] - 130 * 24 * 3600 * 1000;
data = [];
data.push(first);
data.push(last);
}
seriesOptions[i] = {
name: name,
data: data
};
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
and as you can see there are three stocks shown. If you hover the chart with the mouse and go to the beginning you'll notice MSFT stock which has only 2 points and that's intentional. After MSFT there should be about 6 year gap, however on the chart it's shown in a few pixels.
How can I configure stockChart to show real gaps? In other words, I want to see the gap of 6 years so from 2005 till 2011 there will be empty space proportional to the whole chart?
The discussion in the comment section of the first answer reveals that OP wants to hide no-data periods only in some cases.
The solution here might be to set ordinal to false (as #ewolden) suggested and use breaks instead:
xAxis: {
breaks: [{
breakSize: 24 * 3600 * 1000,
from: Date.UTC(2017, 0, 6),
to: Date.UTC(2017, 0, 9)
}],
ordinal: false
},
series: [{
data: [
[Date.UTC(2017, 0, 2), 6],
[Date.UTC(2017, 0, 3), 7],
[Date.UTC(2017, 0, 4), 3],
[Date.UTC(2017, 0, 5), 4],
[Date.UTC(2017, 0, 6), 1],
[Date.UTC(2017, 0, 9), 8],
[Date.UTC(2017, 0, 10), 9],
[Date.UTC(2017, 6, 1), 4],
[Date.UTC(2017, 6, 2), 5]
]
Example: http://jsfiddle.net/BlackLabel/ocg0dujg/
In the above demo I was able to hide the weekend (7 and 8 Jan) and maintain the space between January and July.
API reference: https://api.highcharts.com/highstock/xAxis.breaks
What you are after is ordinal.
In an ordinal axis, the points are equally spaced in the chart regardless of the actual time or x distance between them. This means that missing data for nights or weekends will not take up space in the chart.
Setting ordinal to false, like this, will give you the gap you are after:
xAxis: {
type: 'datetime',
ordinal: false,
},
There are some other issues with your code, if you look in console, you are getting error 15 which states that highcharts requires data to be sorted. You get this because of how you add the series data to your MSFT series. You add both the x and the y to a single 1D array, which means highcharts tries to plot both your x and y values on the x axis.
I did a workaround that gives it the right format in this fiddle: http://jsfiddle.net/2cps91ka/91/