diff --git a/CHANGELOG.md b/CHANGELOG.md index b19b0b15..39286db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# [1.16.0](https://github.com/javascriptdata/scikit.js/compare/v1.15.0...v1.16.0) (2022-04-19) + + +### Features + +* fixed loadBoston calls. Need to do the others ([05c9d9a](https://github.com/javascriptdata/scikit.js/commit/05c9d9af772c1d197798ea28fdd985e92b6fc5ac)) +* fixed tests ([3f6654d](https://github.com/javascriptdata/scikit.js/commit/3f6654d3aa128cfc0296cd97cb4f672cb2184bc9)) +* remove data loading logic in favor of using dfd.readCSV(url) ([3251738](https://github.com/javascriptdata/scikit.js/commit/3251738e09b9e1af9a354b225033a57b1081f573)) + # [1.15.0](https://github.com/javascriptdata/scikit.js/compare/v1.14.0...v1.15.0) (2022-04-18) diff --git a/package-lock.json b/package-lock.json index db9066e7..60bfe979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scikitjs", - "version": "1.15.0", + "version": "1.16.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "scikitjs", - "version": "1.15.0", + "version": "1.16.0", "hasInstallScript": true, "license": "ISC", "dependencies": { @@ -2697,6 +2697,25 @@ "node": ">= 8" } }, + "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -2880,6 +2899,26 @@ "node": ">=0.10.0" } }, + "node_modules/@octokit/request/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@octokit/rest": { "version": "18.12.0", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", @@ -3500,6 +3539,25 @@ "yarn": ">= 1.3.2" } }, + "node_modules/@tensorflow/tfjs-core/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@tensorflow/tfjs-core/node_modules/seedrandom": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", @@ -3563,6 +3621,25 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/@tensorflow/tfjs/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@tensorflow/tfjs/node_modules/seedrandom": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", @@ -5335,6 +5412,26 @@ "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", "dev": true }, + "node_modules/danfojs-node/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/danfojs-node/node_modules/seedrandom": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", @@ -9704,25 +9801,6 @@ "lodash": "^4.17.21" } }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -14720,7 +14798,8 @@ }, "node_modules/tr46": { "version": "0.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "node_modules/traverse": { "version": "0.6.6", @@ -15169,7 +15248,8 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "node_modules/whatwg-encoding": { "version": "1.0.5", @@ -15190,7 +15270,8 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -17178,6 +17259,14 @@ "yallist": "^4.0.0" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -17323,6 +17412,15 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } } } }, @@ -17721,6 +17819,14 @@ "sprintf-js": "~1.0.2" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "seedrandom": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", @@ -17799,6 +17905,14 @@ "seedrandom": "2.4.3" }, "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "seedrandom": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", @@ -19172,6 +19286,15 @@ } } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, "seedrandom": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", @@ -22195,14 +22318,6 @@ "lodash": "^4.17.21" } }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -25739,7 +25854,9 @@ } }, "tr46": { - "version": "0.0.3" + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, "traverse": { "version": "0.6.6", @@ -26026,7 +26143,9 @@ } }, "webidl-conversions": { - "version": "3.0.1" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "whatwg-encoding": { "version": "1.0.5", @@ -26047,6 +26166,8 @@ }, "whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index a9b31f52..9cd76462 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scikitjs", - "version": "1.15.0", + "version": "1.16.0", "description": "Scikit-Learn for JS", "output": { "node": "dist/node/index.js", diff --git a/src/datasets/datasets.ts b/src/datasets/datasets.ts index 60377547..88b7bead 100644 --- a/src/datasets/datasets.ts +++ b/src/datasets/datasets.ts @@ -1,101 +1,9 @@ -/** - * Loads the Boston housing dataset (regression). Samples 506, features 13. - * @example - * ```typescript - import { loadBoston } from 'scikitjs' - - let df = await loadBoston() - df.print() - ``` - */ -export function loadBoston(): string { - return 'http://scikitjs.org/data/boston.csv' -} - -/** - * Loads the Iris dataset (classification). - * This is a very easy multi-class classification dataset. Samples 150, Classes 3, Features 4. - * @example - * ```typescript - import { loadIris } from 'scikitjs' - - let df = await loadIris() - df.print() - ``` - */ -export function loadIris(): string { - return 'http://scikitjs.org/data/iris.csv' -} - -/** - * Loads the Wine dataset (classification). - * This is a very easy multi-class classification dataset. Samples 178, Classes 3, Features 13. - * @example - * ```typescript - import { loadWine } from 'scikitjs' - - let df = await loadWine() - df.print() - ``` - */ - -export function loadWine(): string { - return 'http://scikitjs.org/data/wine.csv' -} - -/** - * Loads the Diabetes dataset (regression). - * Samples 442, Features 10. - * @example - * ```typescript - import { loadDiabetes } from 'scikitjs' - - let df = await loadDiabetes() - df.print() - ``` - */ - -export function loadDiabetes(): string { - return 'http://scikitjs.org/data/diabetes.csv' -} - -/** - * Loads the Breast Cancer Wisconsin dataset (classification). - * Samples 569, Features 30. - * @example - * ```typescript - import { loadBreastCancer } from 'scikitjs' - - let df = await loadBreastCancer() - df.print() - ``` - */ - -export function loadBreastCancer(): string { - return 'http://scikitjs.org/data/breast_cancer.csv' -} - -/** - * Loads the Digit dataset (classification). - * Samples 1797, Features 64. Each sample is an 8x8 image - * @example - * ```typescript - import { loadDigits } from 'scikitjs' - - let df = await loadDigits() - df.print() - ``` - */ -export function loadDigits(): string { - return 'http://scikitjs.org/data/digits.csv' -} - -/** - * Loads the California housing dataset (regression). - * - * Samples 20640, Features 8. - */ - -export function fetchCaliforniaHousing(): string { - return 'http://scikitjs.org/data/california_housing.csv' +export const dataUrls = { + loadBoston: 'http://scikitjs.org/data/boston.csv', + loadIris: 'http://scikitjs.org/data/iris.csv', + loadWine: 'http://scikitjs.org/data/wine.csv', + loadDiabetes: 'http://scikitjs.org/data/diabetes.csv', + loadBreastCancer: 'http://scikitjs.org/data/breast_cancer.csv', + loadDigits: 'http://scikitjs.org/data/digits.csv', + fetchCaliforniaHousing: 'http://scikitjs.org/data/california_housing.csv' } diff --git a/src/index.ts b/src/index.ts index ddc10162..69172976 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,7 +58,7 @@ export { export { RobustScaler, RobustScalerParams } from './preprocessing/robustScaler' export { KMeans, KMeansParams } from './cluster/kmeans' export { Scikit1D, Scikit2D, ScikitVecOrMatrix } from './types' -export * as datasets from './datasets/datasets' +export { dataUrls } from './datasets/datasets' export { makeVotingRegressor, VotingRegressor, diff --git a/src/neighbors/kNeighborsClassifier.test.ts b/src/neighbors/kNeighborsClassifier.test.ts index 5a512e66..0226bf88 100644 --- a/src/neighbors/kNeighborsClassifier.test.ts +++ b/src/neighbors/kNeighborsClassifier.test.ts @@ -15,12 +15,7 @@ import { KNeighborsClassifier } from './kNeighborsClassifier' import { KNeighborsParams } from './kNeighborsBase' -import { - loadDigits, - loadIris, - loadWine, - loadBreastCancer -} from '../datasets/datasets' +import { dataUrls } from '../datasets/datasets' import { crossValScore } from '../model_selection/crossValScore' import { KFold } from '../model_selection/kFold' import { arrayEqual } from '../utils' @@ -31,15 +26,16 @@ type Tensor1D = tf.Tensor1D type Tensor2D = tf.Tensor2D function testWithDataset( - loadData: () => string, + loadDataUrl: string, + loadDataName: string, params: KNeighborsParams, referenceAccuracy: number ) { it( - `matches sklearn fitting ${loadData.name}`.padEnd(48) + + `matches sklearn fitting ${loadDataName}`.padEnd(48) + JSON.stringify(params), async () => { - const df = await dfd.readCSV(loadData()) + const df = await dfd.readCSV(loadDataUrl) const Xy = df.tensor as unknown as Tensor2D let [nSamples, nFeatures] = Xy.shape @@ -72,46 +68,54 @@ for (const algorithm of [ ]) { describe(`KNeighborsClassifier({ algorithm: ${algorithm} })`, () => { testWithDataset( - loadIris, + dataUrls.loadIris, + 'loadIris', { nNeighbors: 5, weights: 'distance', algorithm }, 0.0 ) testWithDataset( - loadIris, + dataUrls.loadIris, + 'loadIris', { nNeighbors: 3, weights: 'uniform', algorithm }, 0.0 ) testWithDataset( - loadWine, + dataUrls.loadWine, + 'loadWine', { nNeighbors: 5, weights: 'distance', algorithm }, 0.135 ) testWithDataset( - loadWine, + dataUrls.loadWine, + 'loadWine', { nNeighbors: 3, weights: 'uniform', algorithm }, 0.158 ) testWithDataset( - loadBreastCancer, + dataUrls.loadBreastCancer, + 'loadBreastCancer', { nNeighbors: 5, weights: 'distance', algorithm }, 0.92 ) testWithDataset( - loadBreastCancer, + dataUrls.loadBreastCancer, + 'loadBreastCancer', { nNeighbors: 3, weights: 'uniform', algorithm }, 0.916 ) if ('brute' !== algorithm) { testWithDataset( - loadDigits, + dataUrls.loadDigits, + 'loadDigits', { nNeighbors: 5, weights: 'distance', algorithm, leafSize: 256 }, 0.963 ) testWithDataset( - loadDigits, + dataUrls.loadDigits, + 'loadDigits', { nNeighbors: 3, weights: 'uniform', algorithm, leafSize: 256 }, 0.967 ) diff --git a/src/neighbors/kNeighborsRegressor.test.ts b/src/neighbors/kNeighborsRegressor.test.ts index 4b7001d2..58dd53fa 100644 --- a/src/neighbors/kNeighborsRegressor.test.ts +++ b/src/neighbors/kNeighborsRegressor.test.ts @@ -15,7 +15,7 @@ import { KNeighborsRegressor } from './kNeighborsRegressor' import { KNeighborsParams } from './kNeighborsBase' -import { fetchCaliforniaHousing, loadDiabetes } from '../datasets/datasets' +import { dataUrls } from '../datasets/datasets' import { arrayEqual } from '../utils' import { crossValScore } from '../model_selection/crossValScore' import { KFold } from '../model_selection/kFold' @@ -28,15 +28,16 @@ type Tensor1D = tf.Tensor1D type Tensor2D = tf.Tensor2D function testWithDataset( - loadData: () => string, + loadDataUrl: string, + loadDataName: string, params: KNeighborsParams, referenceError: number ) { it( - `matches sklearn fitting ${loadData.name}`.padEnd(48) + + `matches sklearn fitting ${loadDataName}`.padEnd(48) + JSON.stringify(params), async () => { - const df = await dfd.readCSV(loadData()) + const df = await dfd.readCSV(loadDataUrl) const Xy = df.tensor as unknown as Tensor2D let [nSamples, nFeatures] = Xy.shape @@ -70,32 +71,37 @@ for (const algorithm of [ ]) { describe(`KNeighborsRegressor({ algorithm: ${algorithm} })`, function () { testWithDataset( - loadDiabetes, + dataUrls.loadDiabetes, + 'loadDiabetes', { nNeighbors: 5, weights: 'distance', algorithm }, 3570 ) testWithDataset( - loadDiabetes, + dataUrls.loadDiabetes, + 'loadDiabetes', { nNeighbors: 3, weights: 'uniform', algorithm }, 3833 ) if ('kdTree' === algorithm) { testWithDataset( - fetchCaliforniaHousing, + dataUrls.fetchCaliforniaHousing, + 'fetchCaliforniaHousing', { nNeighbors: 3, weights: 'distance', algorithm }, 1.31 ) } if ('auto' === algorithm) { testWithDataset( - fetchCaliforniaHousing, + dataUrls.fetchCaliforniaHousing, + 'fetchCaliforniaHousing', { nNeighbors: 4, weights: 'uniform', algorithm, p: 1 }, 1.19 ) } if (undefined === algorithm) { testWithDataset( - fetchCaliforniaHousing, + dataUrls.fetchCaliforniaHousing, + 'fetchCaliforniaHousing', { nNeighbors: 4, weights: 'uniform', algorithm, p: Infinity }, 1.32 ) diff --git a/src/tree/decisiontree.test.ts b/src/tree/decisiontree.test.ts index 3e496508..cb500392 100644 --- a/src/tree/decisiontree.test.ts +++ b/src/tree/decisiontree.test.ts @@ -1,5 +1,5 @@ import { DecisionTreeClassifier, DecisionTreeRegressor } from './decisiontree' -import { loadBoston, loadIris } from '../datasets/datasets' +import { dataUrls } from '../datasets/datasets' import * as dfd from 'danfojs-node' describe('DecisionTree', function () { @@ -424,7 +424,7 @@ describe('DecisionTree', function () { } ] /*[[[end]]]*/ - let df = await dfd.readCSV(loadIris()) + let df = await dfd.readCSV(dataUrls.loadIris) let y = df['target'].values let X = df.drop({ columns: 'target' }).values @@ -586,7 +586,7 @@ describe('DecisionTree', function () { } ] /*[[[end]]]*/ - let df = await dfd.readCSV(loadBoston()) + let df = await dfd.readCSV(dataUrls.loadBoston) let y = df['target'].values let X = df.drop({ columns: 'target' }).values
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: