aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Justine Tunney <jart@google.com>2016-08-22 13:03:29 -0800
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2016-08-22 14:18:16 -0700
commitd1abf00728cb42ac341a3614b2038aad89f8279e (patch)
tree484698355e81f9d9c944c127489ee684430bcde2
parentcd95f3a7d66833cdb55fa180d5002f2cf686bcef (diff)
Use asciinumerical sort for tooltips and more
In order to accomplish this, I refactored the new sorting algorithm out into a separate tf-sorting component. Change: 130979195
-rw-r--r--tensorflow/tensorboard/components/tf-backend/backend.ts58
-rw-r--r--tensorflow/tensorboard/components/tf-backend/test/backendTests.ts52
-rw-r--r--tensorflow/tensorboard/components/tf-backend/test/index.html1
-rw-r--r--tensorflow/tensorboard/components/tf-backend/tf-backend.html1
-rw-r--r--tensorflow/tensorboard/components/tf-dashboard-common/categorizer.ts4
-rw-r--r--tensorflow/tensorboard/components/tf-dashboard-common/test/index.html1
-rw-r--r--tensorflow/tensorboard/components/tf-dashboard-common/tf-categorizer.html1
-rw-r--r--tensorflow/tensorboard/components/tf-event-dashboard/tf-event-dashboard.html2
-rw-r--r--tensorflow/tensorboard/components/tf-globals/tf-globals.html2
-rw-r--r--tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.html4
-rw-r--r--tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.ts7
-rw-r--r--tensorflow/tensorboard/components/vz-sorting/sorting.ts78
-rw-r--r--tensorflow/tensorboard/components/vz-sorting/test/index.html22
-rw-r--r--tensorflow/tensorboard/components/vz-sorting/test/sortingTests.ts71
-rw-r--r--tensorflow/tensorboard/components/vz-sorting/vz-sorting.html17
15 files changed, 206 insertions, 115 deletions
diff --git a/tensorflow/tensorboard/components/tf-backend/backend.ts b/tensorflow/tensorboard/components/tf-backend/backend.ts
index 5c75d79010..39c967b239 100644
--- a/tensorflow/tensorboard/components/tf-backend/backend.ts
+++ b/tensorflow/tensorboard/components/tf-backend/backend.ts
@@ -260,64 +260,12 @@ module TF.Backend {
/** Given a RunToTag, return sorted array of all runs */
export function getRuns(r: RunToTag): string[] {
- return _.keys(r).sort(compareTagNames);
+ return _.keys(r).sort(VZ.Sorting.compareTagNames);
}
/** Given a RunToTag, return array of all tags (sorted + dedup'd) */
export function getTags(r: RunToTag): string[] {
- return _.union.apply(null, _.values(r)).sort(compareTagNames);
- }
-
- /** Compares tag names asciinumerically broken into components. */
- export function compareTagNames(a, b: string): number {
- let ai = 0;
- let bi = 0;
- while (true) {
- if (ai === a.length) return bi === b.length ? 0 : -1;
- if (bi === b.length) return 1;
- if (isDigit(a[ai]) && isDigit(b[bi])) {
- let ais = ai;
- let bis = bi;
- ai = consumeNumber(a, ai + 1);
- bi = consumeNumber(b, bi + 1);
- let an = parseFloat(a.slice(ais, ai));
- let bn = parseFloat(b.slice(bis, bi));
- if (an < bn) return -1;
- if (an > bn) return 1;
- continue;
- }
- if (isBreak(a[ai])) {
- if (!isBreak(b[bi])) return -1;
- } else if (isBreak(b[bi])) {
- return 1;
- } else if (a[ai] < b[bi]) {
- return -1;
- } else if (a[ai] > b[bi]) {
- return 1;
- }
- ai++;
- bi++;
- }
- }
-
- function consumeNumber(s: string, i: number): number {
- let decimal = false;
- for (; i < s.length; i++) {
- if (isDigit(s[i])) continue;
- if (!decimal && s[i] === '.') {
- decimal = true;
- continue;
- }
- break;
- }
- return i;
- }
-
- function isDigit(c: string): boolean { return '0' <= c && c <= '9'; }
-
- function isBreak(c: string): boolean {
- // TODO(jart): Remove underscore when people stop using it like a slash.
- return c === '/' || c === '_' || isDigit(c);
+ return _.union.apply(null, _.values(r)).sort(VZ.Sorting.compareTagNames);
}
/**
@@ -328,7 +276,7 @@ module TF.Backend {
export function filterTags(r: RunToTag, runs: string[]): string[] {
var result = [];
runs.forEach((x) => result = result.concat(r[x]));
- return _.uniq(result).sort();
+ return _.uniq(result).sort(VZ.Sorting.compareTagNames);
}
function timeToDate(x: number): Date { return new Date(x * 1000); };
diff --git a/tensorflow/tensorboard/components/tf-backend/test/backendTests.ts b/tensorflow/tensorboard/components/tf-backend/test/backendTests.ts
index 15cdd0bf95..9df5399db5 100644
--- a/tensorflow/tensorboard/components/tf-backend/test/backendTests.ts
+++ b/tensorflow/tensorboard/components/tf-backend/test/backendTests.ts
@@ -277,56 +277,4 @@ module TF.Backend {
assertHistogramEquality(newHistogram, histogram);
});
});
-
- describe('sortTagNames', () => {
-
- let sortTagNames = (a) => a.sort(compareTagNames);
-
- it('is asciibetical', () => {
- assert.deepEqual(sortTagNames(['a', 'b']), ['a', 'b']);
- assert.deepEqual(sortTagNames(['a', 'B']), ['B', 'a']);
- });
-
- it('sorts integer portions', () => {
- assert.deepEqual(['03', '1'].sort(), ['03', '1']);
- assert.deepEqual(sortTagNames(['03', '1']), ['1', '03']);
- assert.deepEqual(sortTagNames(['a03', 'a1']), ['a1', 'a03']);
- assert.deepEqual(sortTagNames(['a03', 'b1']), ['a03', 'b1']);
- assert.deepEqual(sortTagNames(['x0a03', 'x0a1']), ['x0a1', 'x0a03']);
- assert.deepEqual(sortTagNames(['a/b/03', 'a/b/1']), ['a/b/1', 'a/b/03']);
- });
-
- it('sorts floating point portions', () => {
- assert.deepEqual(sortTagNames(['a0.1', 'a0.01']), ['a0.01', 'a0.1']);
- });
-
- it('is componentized by slash', () => {
- assert.deepEqual(['a+/a', 'a/a', 'ab/a'].sort(), ['a+/a', 'a/a', 'ab/a']);
- assert.deepEqual(
- sortTagNames(['a+/a', 'a/a', 'ab/a']), ['a/a', 'a+/a', 'ab/a']);
- });
-
- it('is componentized by underscore', () => {
- assert.deepEqual(
- sortTagNames(['a+_a', 'a_a', 'ab_a']), ['a_a', 'a+_a', 'ab_a']);
- assert.deepEqual(
- sortTagNames(['a+/a', 'a_a', 'ab_a']), ['a_a', 'a+/a', 'ab_a']);
- });
-
- it('is componentized by number boundaries', () => {
- assert.deepEqual(
- sortTagNames(['a+0a', 'a0a', 'ab0a']), ['a0a', 'a+0a', 'ab0a']);
- });
-
- it('empty comes first', () => {
- assert.deepEqual(
- sortTagNames(['a', '//', '/', '']), ['', '/', '//', 'a']);
- });
-
- it('decimal parsed correctly', () => {
- assert.deepEqual(sortTagNames(['0.2', '0.03']), ['0.03', '0.2']);
- assert.deepEqual(sortTagNames(['0..2', '0..03']), ['0..2', '0..03']);
- assert.deepEqual(sortTagNames(['.2', '.03']), ['.2', '.03']);
- });
- });
}
diff --git a/tensorflow/tensorboard/components/tf-backend/test/index.html b/tensorflow/tensorboard/components/tf-backend/test/index.html
index c97873f46a..a1ee080b0e 100644
--- a/tensorflow/tensorboard/components/tf-backend/test/index.html
+++ b/tensorflow/tensorboard/components/tf-backend/test/index.html
@@ -20,6 +20,7 @@ limitations under the License.
<script src="../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../tf-imports/d3.html">
+ <link rel="import" href="../../vz-sorting/vz-sorting.html">
</head>
<body>
<test-fixture id="testElementFixture">
diff --git a/tensorflow/tensorboard/components/tf-backend/tf-backend.html b/tensorflow/tensorboard/components/tf-backend/tf-backend.html
index bc4f16a5f7..d416787e3f 100644
--- a/tensorflow/tensorboard/components/tf-backend/tf-backend.html
+++ b/tensorflow/tensorboard/components/tf-backend/tf-backend.html
@@ -1,6 +1,7 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../tf-imports/lodash.html">
<link rel="import" href="../tf-imports/d3.html">
+<link rel="import" href="../vz-sorting/vz-sorting.html">
<script src="requestManager.js"></script>
<script src="urlPathHelpers.js"></script>
diff --git a/tensorflow/tensorboard/components/tf-dashboard-common/categorizer.ts b/tensorflow/tensorboard/components/tf-dashboard-common/categorizer.ts
index d9c477e744..5131b108e4 100644
--- a/tensorflow/tensorboard/components/tf-dashboard-common/categorizer.ts
+++ b/tensorflow/tensorboard/components/tf-dashboard-common/categorizer.ts
@@ -81,7 +81,7 @@ module Categorizer {
if (tags.length === 0) {
return [];
}
- var sortedTags = tags.slice().sort();
+ var sortedTags = tags.slice().sort(VZ.Sorting.compareTagNames);
var categories: Category[] = [];
var currentCategory = {
name: extractor(sortedTags[0]),
@@ -134,7 +134,7 @@ module Categorizer {
tags.push(t);
}
});
- var cat = { name: def.name, tags: tags.sort() };
+ var cat = {name: def.name, tags: tags.sort(VZ.Sorting.compareTagNames)};
return cat;
});
var defaultCategories = fallback(remaining.values());
diff --git a/tensorflow/tensorboard/components/tf-dashboard-common/test/index.html b/tensorflow/tensorboard/components/tf-dashboard-common/test/index.html
index fd4a097708..6336c876b8 100644
--- a/tensorflow/tensorboard/components/tf-dashboard-common/test/index.html
+++ b/tensorflow/tensorboard/components/tf-dashboard-common/test/index.html
@@ -5,6 +5,7 @@
<script src="../../webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../web-component-tester/browser.js"></script>
<link rel="import" href="../../tf-imports/d3.html">
+ <link rel="import" href="../../vz-sorting/vz-sorting.html">
</head>
<body>
<script src="../categorizer.js"></script>
diff --git a/tensorflow/tensorboard/components/tf-dashboard-common/tf-categorizer.html b/tensorflow/tensorboard/components/tf-dashboard-common/tf-categorizer.html
index 1c3a3f811e..9eeef71a23 100644
--- a/tensorflow/tensorboard/components/tf-dashboard-common/tf-categorizer.html
+++ b/tensorflow/tensorboard/components/tf-dashboard-common/tf-categorizer.html
@@ -3,6 +3,7 @@
<link rel="import" href="../tf-storage/tf-storage.html">
<link rel="import" href="tf-regex-group.html">
<link rel="import" href="tensorboard-color.html">
+<link rel="import" href="../vz-sorting/vz-sorting.html">
<!--
`tf-categorizer` turns an array of tags into an array of categories
diff --git a/tensorflow/tensorboard/components/tf-event-dashboard/tf-event-dashboard.html b/tensorflow/tensorboard/components/tf-event-dashboard/tf-event-dashboard.html
index d3000e4616..db9089280e 100644
--- a/tensorflow/tensorboard/components/tf-event-dashboard/tf-event-dashboard.html
+++ b/tensorflow/tensorboard/components/tf-event-dashboard/tf-event-dashboard.html
@@ -60,7 +60,7 @@ The #center div contains tf-line-charts embedded inside tf-collapsable-panes.
selected-item-label="{{_tooltipSortingMethod}}"
>
<paper-menu class="dropdown-content" selected="0">
- <paper-item>name</paper-item>
+ <paper-item>default</paper-item>
<paper-item>descending</paper-item>
<paper-item>ascending</paper-item>
</paper-menu>
diff --git a/tensorflow/tensorboard/components/tf-globals/tf-globals.html b/tensorflow/tensorboard/components/tf-globals/tf-globals.html
index 3195518f37..a76f21d96c 100644
--- a/tensorflow/tensorboard/components/tf-globals/tf-globals.html
+++ b/tensorflow/tensorboard/components/tf-globals/tf-globals.html
@@ -14,6 +14,6 @@ limitations under the License.
=============================================================================-->
<dom-module id="tf-globals">
- <script src="globals.js"></script>
+ <script src="globals.js"></script>
</dom-module>
diff --git a/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.html b/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.html
index 9ca063c082..1d68cc90f7 100644
--- a/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.html
+++ b/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.html
@@ -174,13 +174,13 @@ such as different X scales (linear and temporal), tooltips and smoothing.
/**
* Change how the tooltip is sorted. Allows:
- * - "name" - Sort the tooltip by series name.
+ * - "default" - Sort the tooltip by input order.
* - "ascending" - Sort the tooltip by ascending value.
* - "descending" - Sort the tooltip by descending value.
*/
tooltipSortingMethod: {
type: String,
- value: 'name'
+ value: 'default'
},
/**
diff --git a/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.ts b/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.ts
index 8e15200011..6fde2706f1 100644
--- a/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.ts
+++ b/tensorflow/tensorboard/components/vz-line-chart/vz-line-chart.ts
@@ -304,8 +304,11 @@ module VZ {
points =
_.sortBy(points, (d) => valueSortMethod(d.datum, -1, d.dataset))
.reverse();
- } else { // Sort by 'name'
- points = _.sortBy(points, (d) => d.dataset.metadata().name);
+ } else {
+ // The 'default' sorting method maintains the order of names passed to
+ // setVisibleSeries(). However we reverse that order when defining the
+ // datasets. So we must call reverse again to restore the order.
+ points = points.slice(0).reverse();
}
let rows = this.tooltip.select('tbody')
diff --git a/tensorflow/tensorboard/components/vz-sorting/sorting.ts b/tensorflow/tensorboard/components/vz-sorting/sorting.ts
new file mode 100644
index 0000000000..53dd349e9d
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz-sorting/sorting.ts
@@ -0,0 +1,78 @@
+/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+module VZ.Sorting {
+
+ /**
+ * Compares tag names asciinumerically broken into components.
+ *
+ * <p>This is the comparison function used for sorting most string values in
+ * TensorBoard. Unlike the standard asciibetical comparator, this function
+ * knows that 'a10b' > 'a2b'. Numbers with decimal points are supported. It
+ * also splits the input by slash and underscore and performs an array
+ * sort. Therefore it knows that 'a/a' < 'a+/a' even though '+' < '/' in the
+ * ASCII table.
+ */
+ export function compareTagNames(a, b: string): number {
+ let ai = 0;
+ let bi = 0;
+ while (true) {
+ if (ai === a.length) return bi === b.length ? 0 : -1;
+ if (bi === b.length) return 1;
+ if (isDigit(a[ai]) && isDigit(b[bi])) {
+ const ais = ai;
+ const bis = bi;
+ ai = consumeNumber(a, ai + 1);
+ bi = consumeNumber(b, bi + 1);
+ const an = parseFloat(a.slice(ais, ai));
+ const bn = parseFloat(b.slice(bis, bi));
+ if (an < bn) return -1;
+ if (an > bn) return 1;
+ continue;
+ }
+ if (isBreak(a[ai])) {
+ if (!isBreak(b[bi])) return -1;
+ } else if (isBreak(b[bi])) {
+ return 1;
+ } else if (a[ai] < b[bi]) {
+ return -1;
+ } else if (a[ai] > b[bi]) {
+ return 1;
+ }
+ ai++;
+ bi++;
+ }
+ }
+
+ function consumeNumber(s: string, i: number): number {
+ let decimal = false;
+ for (; i < s.length; i++) {
+ if (isDigit(s[i])) continue;
+ if (!decimal && s[i] === '.') {
+ decimal = true;
+ continue;
+ }
+ break;
+ }
+ return i;
+ }
+
+ function isDigit(c: string): boolean { return '0' <= c && c <= '9'; }
+
+ function isBreak(c: string): boolean {
+ // TODO(jart): Remove underscore when people stop using it like a slash.
+ return c === '/' || c === '_' || isDigit(c);
+ }
+}
diff --git a/tensorflow/tensorboard/components/vz-sorting/test/index.html b/tensorflow/tensorboard/components/vz-sorting/test/index.html
new file mode 100644
index 0000000000..9ba04e0136
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz-sorting/test/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+ Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<meta charset="utf-8">
+<script src="../../web-component-tester/browser.js"></script>
+<body>
+<script src="../sorting.js"></script>
+<script src="sortingTests.js"></script>
diff --git a/tensorflow/tensorboard/components/vz-sorting/test/sortingTests.ts b/tensorflow/tensorboard/components/vz-sorting/test/sortingTests.ts
new file mode 100644
index 0000000000..0a24463d8a
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz-sorting/test/sortingTests.ts
@@ -0,0 +1,71 @@
+/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+module VZ.Sorting {
+
+ const assert = chai.assert;
+
+ describe('compareTagNames', () => {
+
+ const sortTagNames = (a) => a.sort(compareTagNames);
+
+ it('is asciibetical', () => {
+ assert.deepEqual(sortTagNames(['a', 'b']), ['a', 'b']);
+ assert.deepEqual(sortTagNames(['a', 'B']), ['B', 'a']);
+ });
+
+ it('sorts integer portions', () => {
+ assert.deepEqual(['03', '1'].sort(), ['03', '1']);
+ assert.deepEqual(sortTagNames(['03', '1']), ['1', '03']);
+ assert.deepEqual(sortTagNames(['a03', 'a1']), ['a1', 'a03']);
+ assert.deepEqual(sortTagNames(['a03', 'b1']), ['a03', 'b1']);
+ assert.deepEqual(sortTagNames(['x0a03', 'x0a1']), ['x0a1', 'x0a03']);
+ assert.deepEqual(sortTagNames(['a/b/03', 'a/b/1']), ['a/b/1', 'a/b/03']);
+ });
+
+ it('sorts floating point portions', () => {
+ assert.deepEqual(sortTagNames(['a0.1', 'a0.01']), ['a0.01', 'a0.1']);
+ });
+
+ it('is componentized by slash', () => {
+ assert.deepEqual(['a+/a', 'a/a', 'ab/a'].sort(), ['a+/a', 'a/a', 'ab/a']);
+ assert.deepEqual(
+ sortTagNames(['a+/a', 'a/a', 'ab/a']), ['a/a', 'a+/a', 'ab/a']);
+ });
+
+ it('is componentized by underscore', () => {
+ assert.deepEqual(
+ sortTagNames(['a+_a', 'a_a', 'ab_a']), ['a_a', 'a+_a', 'ab_a']);
+ assert.deepEqual(
+ sortTagNames(['a+/a', 'a_a', 'ab_a']), ['a_a', 'a+/a', 'ab_a']);
+ });
+
+ it('is componentized by number boundaries', () => {
+ assert.deepEqual(
+ sortTagNames(['a+0a', 'a0a', 'ab0a']), ['a0a', 'a+0a', 'ab0a']);
+ });
+
+ it('empty comes first', () => {
+ assert.deepEqual(
+ sortTagNames(['a', '//', '/', '']), ['', '/', '//', 'a']);
+ });
+
+ it('decimal parsed correctly', () => {
+ assert.deepEqual(sortTagNames(['0.2', '0.03']), ['0.03', '0.2']);
+ assert.deepEqual(sortTagNames(['0..2', '0..03']), ['0..2', '0..03']);
+ assert.deepEqual(sortTagNames(['.2', '.03']), ['.2', '.03']);
+ });
+ });
+}
diff --git a/tensorflow/tensorboard/components/vz-sorting/vz-sorting.html b/tensorflow/tensorboard/components/vz-sorting/vz-sorting.html
new file mode 100644
index 0000000000..9376663dae
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz-sorting/vz-sorting.html
@@ -0,0 +1,17 @@
+<!--
+ Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<script src="sorting.js"></script>