release(camera-ai): publish 0.4.12 catalog portability fix

This commit is contained in:
KINDNICK
2026-08-16 01:05:07 +09:00
parent 8e5e2e8ad0
commit b65e54a7e8
211 changed files with 45990 additions and 45913 deletions
+3 -1
View File
@@ -1,6 +1,8 @@
*.meta -whitespace *.meta -whitespace
CameraAI~/Tools~/CWCameraWorker/** -diff -whitespace CameraAI~/Tools~/CWCameraWorker/** -text -diff -whitespace
*.exe filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text
*.dll filter=lfs diff=lfs merge=lfs -text *.dll filter=lfs diff=lfs merge=lfs -text
*.pyd filter=lfs diff=lfs merge=lfs -text *.pyd filter=lfs diff=lfs merge=lfs -text
CameraAI~/RuntimeData~/DatasetExports/**/*.npz filter=lfs diff=lfs merge=lfs -text CameraAI~/RuntimeData~/DatasetExports/**/*.npz filter=lfs diff=lfs merge=lfs -text
CameraAI~/RuntimeData~/DatasetExports/dataset_catalog.json text eol=lf
CameraAI~/RuntimeData~/reports/training_index.json text eol=lf
+11
View File
@@ -1,5 +1,16 @@
# Changelog # Changelog
## 0.4.12 - 2026-08-16
- Made packaged catalog freshness checks portable across Windows Git
checkouts: an exact raw hash remains authoritative, while a byte-for-byte
equivalent CRLF checkout is accepted only when LF normalization reproduces
the indexed hash. Content changes and Git LFS pointer files still fail
closed.
- Extended `CWCameraWorker doctor` to validate the packaged training index
against its catalog, and pinned the release catalog and training index to LF
line endings. Rebuilt the self-contained Windows worker as 0.1.8.
## 0.4.11 - 2026-08-10 ## 0.4.11 - 2026-08-10
- Integrated the verified YAMO dataset refresh without replacing the packaged - Integrated the verified YAMO dataset refresh without replacing the packaged
@@ -17,7 +17,7 @@ must remain together.
In Unity Package Manager, choose **Add package from git URL** and enter: In Unity Package Manager, choose **Add package from git URL** and enter:
```text ```text
https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.17 https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.18
``` ```
The initial package download is large because the frozen Windows worker is The initial package download is large because the frozen Windows worker is
@@ -26,7 +26,7 @@ it, remove the package lock entry, and add the package again.
## Reference data ## Reference data
Version 0.1.17 includes a compact, read-only `RuntimeData~` bundle with the Version 0.1.18 includes a compact, read-only `RuntimeData~` bundle with the
263 prepared reference songs, cut policy, and ranker model. An artist 263 prepared reference songs, cut policy, and ranker model. An artist
workstation does not need a separate `CW-AI` checkout or Python installation. workstation does not need a separate `CW-AI` checkout or Python installation.
If a newer access-controlled library is available, it remains an optional If a newer access-controlled library is available, it remains an optional
+1 -1
View File
@@ -16,7 +16,7 @@ Cinemachine Track이나 기존 카메라 애니메이션은 필요하지 않으
Unity Package Manager의 `Add package from git URL`에는 다음 주소를 사용할 수 Unity Package Manager의 `Add package from git URL`에는 다음 주소를 사용할 수
있습니다. 있습니다.
`https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.17` `https://kindnick-git.duckdns.org/mingle/streamingle-unity-utilities.git?path=/CameraAI~#v0.1.18`
배포 패키지에는 Windows x64용 `CWCameraWorker` 폴더 전체가 포함됩니다. 배포 패키지에는 Windows x64용 `CWCameraWorker` 폴더 전체가 포함됩니다.
Python은 따로 설치하지 않아도 되지만, Git 패키지의 대용량 바이너리를 받으려면 Python은 따로 설치하지 않아도 되지만, Git 패키지의 대용량 바이너리를 받으려면
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,39 +1,39 @@
{ {
"schemaVersion": "cw-camera-worker-build-identity-v1", "schemaVersion": "cw-camera-worker-build-identity-v1",
"workerVersion": "0.1.7", "workerVersion": "0.1.8",
"createdUtc": "2026-08-09T23:32:08.208961+00:00", "createdUtc": "2026-08-15T15:56:01.810396+00:00",
"python": "3.12.13", "python": "3.12.13",
"sourceRootRelative": "cwai_sources/repository", "sourceRootRelative": "cwai_sources/repository",
"sourceSha256": { "sourceSha256": {
"MachineLearning/CameraDirector/adjacent_transition.py": "6c1b62c2996960af23d62e9c3acd3114600effda03273b006f7076c467c4bdfa", "MachineLearning/CameraDirector/adjacent_transition.py": "6c1b62c2996960af23d62e9c3acd3114600effda03273b006f7076c467c4bdfa",
"MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "427b3cd8fa6eb444517281ef2e21ebc5d751985f4ad271eee49617fda0fb7b33", "MachineLearning/CameraDirector/build_hybrid_preparation_cache.py": "427b3cd8fa6eb444517281ef2e21ebc5d751985f4ad271eee49617fda0fb7b33",
"MachineLearning/CameraDirector/camera_kinematics.py": "e94eefcb56c4ebdccbc57ae8d3650109b2c912e3642892ac13add9409db21a7f", "MachineLearning/CameraDirector/camera_kinematics.py": "e94eefcb56c4ebdccbc57ae8d3650109b2c912e3642892ac13add9409db21a7f",
"MachineLearning/CameraDirector/camera_runtime_data.py": "8ef2e84a9d79b24c3f3232b1f18da49741988765044dffc6075f6e6dd5ff001d", "MachineLearning/CameraDirector/camera_runtime_data.py": "8ef2e84a9d79b24c3f3232b1f18da49741988765044dffc6075f6e6dd5ff001d",
"MachineLearning/CameraDirector/cw_camera_cli.py": "943870a4cdec7e4b830b9c945d690e700902cf2a37d1e49987bba85bf0b0ea87", "MachineLearning/CameraDirector/cw_camera_cli.py": "b523fdf6820b3b203dcdef80877a6ad9576fba41d7285c790c300e1813789012",
"MachineLearning/CameraDirector/cw_camera_runtime.py": "7530d24a65a55c235726676b7991b7a375a2739d8b6257f53cc0340f6cc7de1c", "MachineLearning/CameraDirector/cw_camera_runtime.py": "5cc252ff45b2a80dffbeea14ad37013ba4a428c4f6fe31a617957eec6c823429",
"MachineLearning/CameraDirector/data_driven_cut_planner.py": "36cf86a80577278d2e966cb0ebf7c3110b5b1dda5abd1d8a6b0a76e31d10e32b", "MachineLearning/CameraDirector/data_driven_cut_planner.py": "36cf86a80577278d2e966cb0ebf7c3110b5b1dda5abd1d8a6b0a76e31d10e32b",
"MachineLearning/CameraDirector/generate_hybrid.py": "b7e0053563065ebb6d563e72fa0221d31a9e36c1c94c37992f13974b25bb5dfd", "MachineLearning/CameraDirector/generate_hybrid.py": "bd37744b7b6573bf14be4f0ec5202f1465e11bff71ccabec210533e135b546c3",
"MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380", "MachineLearning/CameraDirector/hybrid_candidate_cache.py": "a0a5b6a8f612f18cb2875394f17ed89950e1e62e38c1226457d9e35848f6e380",
"MachineLearning/CameraDirector/hybrid_cut_reference.py": "306e650dc193fd0d9c42fd52d3f14eef112d1a0da2a27c13b88256d42d8542aa", "MachineLearning/CameraDirector/hybrid_cut_reference.py": "306e650dc193fd0d9c42fd52d3f14eef112d1a0da2a27c13b88256d42d8542aa",
"MachineLearning/CameraDirector/hybrid_preparation_cache.py": "e4d062a48cf39e01ea97951d103a37817c860e94f440f49f080c622b4969fb5a", "MachineLearning/CameraDirector/hybrid_preparation_cache.py": "e4d062a48cf39e01ea97951d103a37817c860e94f440f49f080c622b4969fb5a",
"MachineLearning/CameraDirector/hybrid_single_shot_cache.py": "f540e603b0ccffb86ee003d441c3889c245a208db8fd8b2b966fdacfeea30950", "MachineLearning/CameraDirector/hybrid_single_shot_cache.py": "f540e603b0ccffb86ee003d441c3889c245a208db8fd8b2b966fdacfeea30950",
"MachineLearning/CameraDirector/planner.py": "8575788c0ff1e984f8238354dcd6de2dab9f62afeb71fa4b77ebfc7c66400372", "MachineLearning/CameraDirector/planner.py": "8575788c0ff1e984f8238354dcd6de2dab9f62afeb71fa4b77ebfc7c66400372",
"MachineLearning/CameraDirector/shot_features.py": "fd912b00320ea53ca682c010de6ed1102edcd021ceb4822a5785033a500786fe", "MachineLearning/CameraDirector/shot_features.py": "fd912b00320ea53ca682c010de6ed1102edcd021ceb4822a5785033a500786fe",
"MachineLearning/CameraDirector/train.py": "070d1c4159c56a2107ae0d383ab0b716b12c13186d45383e94b83d3ba6305d03", "MachineLearning/CameraDirector/train.py": "070d1c4159c56a2107ae0d383ab0b716b12c13186d45383e94b83d3ba6305d03",
"MachineLearning/CameraDirector/trajectory_quality.py": "20cd2ddc1b2349f8c33f6d1460580f37964a521eaa86ceee5b8ae457512ba49d" "MachineLearning/CameraDirector/trajectory_quality.py": "20cd2ddc1b2349f8c33f6d1460580f37964a521eaa86ceee5b8ae457512ba49d"
}, },
"preparationLogicIdentifier": "1cb808e207d54854267a1020a658812c79e9bad5a5296f78140228b3a15eeb3b", "preparationLogicIdentifier": "1cb808e207d54854267a1020a658812c79e9bad5a5296f78140228b3a15eeb3b",
"candidateLogicIdentifier": "8dfa557f33c17f4b59849431523cef3650091dc2db3c6c48074888f1f6aef143", "candidateLogicIdentifier": "e768283e8496360ff703b5135f85c51520a888787612b15456def131d5ee1d89",
"generationCodeIdentifier": "58a7d5d440e5bea630c060823fea7280d81d533e5c4741259ee974bce27e6eb4", "generationCodeIdentifier": "80e9eeca5aed9e17a9cfb61b235f954ed4eb24308a1a1ca962e424dbb54bea88",
"generationCodeFiles": [ "generationCodeFiles": [
"adjacent_transition.py", "adjacent_transition.py",
"camera_kinematics.py", "camera_kinematics.py",
"camera_runtime_data.py", "camera_runtime_data.py",
"data_driven_cut_planner.py", "data_driven_cut_planner.py",
"generate_hybrid.py", "generate_hybrid.py",
"hybrid_cut_reference.py", "hybrid_cut_reference.py",
"planner.py", "planner.py",
"shot_features.py", "shot_features.py",
"trajectory_quality.py" "trajectory_quality.py"
] ]
} }
@@ -710,11 +710,12 @@ def dependency_checks() -> list[dict[str, Any]]:
def doctor_payload(runtime_root: Path) -> dict[str, Any]: def doctor_payload(runtime_root: Path) -> dict[str, Any]:
runtime_root = Path(runtime_root).resolve() runtime_root = Path(runtime_root).resolve()
training_index_path = runtime_root / "reports" / "training_index.json"
resources = ( resources = (
("datasetRoot", runtime_root / "DatasetExports", "directory"), ("datasetRoot", runtime_root / "DatasetExports", "directory"),
( (
"trainingIndex", "trainingIndex",
runtime_root / "reports" / "training_index.json", training_index_path,
"file", "file",
), ),
( (
@@ -727,6 +728,32 @@ def doctor_payload(runtime_root: Path) -> dict[str, Any]:
for name, path, kind in resources: for name, path, kind in resources:
ok = path.is_dir() if kind == "directory" else path.is_file() ok = path.is_dir() if kind == "directory" else path.is_file()
checks.append({"name": name, "ok": ok, "detail": str(path)}) checks.append({"name": name, "ok": ok, "detail": str(path)})
if training_index_path.is_file():
try:
generator = importlib.import_module("generate_hybrid")
training_index = json.loads(
training_index_path.read_text(encoding="utf-8")
)
catalog_path = generator.validate_training_index_catalog_freshness(
training_index_path,
training_index,
)
except Exception as error:
checks.append(
{
"name": "trainingIndexCatalogFreshness",
"ok": False,
"detail": f"{type(error).__name__}: {error}",
}
)
else:
checks.append(
{
"name": "trainingIndexCatalogFreshness",
"ok": True,
"detail": str(catalog_path),
}
)
if cw_camera_runtime.is_frozen_runtime(): if cw_camera_runtime.is_frozen_runtime():
identity = cw_camera_runtime.load_build_identity() identity = cw_camera_runtime.load_build_identity()
checks.append( checks.append(
@@ -19,7 +19,7 @@ from typing import Any, Iterable
BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1" BUILD_IDENTITY_SCHEMA_VERSION = "cw-camera-worker-build-identity-v1"
BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json" BUILD_IDENTITY_FILE = "cw_camera_worker_build_identity.json"
WORKER_VERSION = "0.1.7" WORKER_VERSION = "0.1.8"
def is_frozen_runtime() -> bool: def is_frozen_runtime() -> bool:
@@ -50,6 +50,10 @@ import cw_camera_runtime
import trajectory_quality import trajectory_quality
GIT_LFS_POINTER_PREFIX = "version https://git-lfs.github.com/spec/v1"
UTF8_BOM = "\ufeff"
HARD_PERSON_SAFE_BOUNDS = { HARD_PERSON_SAFE_BOUNDS = {
"wide": (0.1, 0.9, 0.12, 0.86), "wide": (0.1, 0.9, 0.12, 0.86),
"medium": (0.12, 0.88, 0.12, 0.84), "medium": (0.12, 0.88, 0.12, 0.84),
@@ -8675,8 +8679,41 @@ def validate_training_index_catalog_freshness(
raise FileNotFoundError( raise FileNotFoundError(
f"Training-index catalog was not found. Searched: {searched}" f"Training-index catalog was not found. Searched: {searched}"
) )
actual_sha256 = sha256_file(resolved_catalog).lower() catalog_bytes = resolved_catalog.read_bytes()
if actual_sha256 != expected_sha256: pointer_probe = catalog_bytes
utf8_bom = UTF8_BOM.encode("utf-8")
if pointer_probe.startswith(utf8_bom):
pointer_probe = pointer_probe[len(utf8_bom) :]
if pointer_probe.startswith(GIT_LFS_POINTER_PREFIX.encode("ascii")):
raise ValueError(
"Training-index catalog is still a Git LFS pointer: "
f"{resolved_catalog}. Materialize Git LFS assets and retry."
)
actual_sha256 = hashlib.sha256(catalog_bytes).hexdigest().lower()
catalog_matches = actual_sha256 == expected_sha256
if not catalog_matches:
try:
catalog_bytes.decode("utf-8", errors="strict")
except UnicodeDecodeError:
normalized_sha256 = None
else:
all_cr_bytes_are_crlf = all(
offset + 1 < len(catalog_bytes)
and catalog_bytes[offset + 1] == 0x0A
for offset, value in enumerate(catalog_bytes)
if value == 0x0D
)
if all_cr_bytes_are_crlf:
normalized_bytes = catalog_bytes.replace(b"\r\n", b"\n")
normalized_sha256 = hashlib.sha256(
normalized_bytes
).hexdigest().lower()
else:
normalized_sha256 = None
catalog_matches = normalized_sha256 == expected_sha256
if not catalog_matches:
raise ValueError( raise ValueError(
"Training index is stale: catalog SHA-256 mismatch for " "Training index is stale: catalog SHA-256 mismatch for "
f"{resolved_catalog}. Expected {expected_sha256}, got {actual_sha256}. " f"{resolved_catalog}. Expected {expected_sha256}, got {actual_sha256}. "
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,32 +1,32 @@
importlib_metadata-8.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 importlib_metadata-8.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
importlib_metadata-8.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 importlib_metadata-8.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
importlib_metadata-8.0.0.dist-info/METADATA,sha256=anuQ7_7h4J1bSEzfcjIBakPi2cyVQ7y7jklLHsBeH1k,4648 importlib_metadata-8.0.0.dist-info/METADATA,sha256=anuQ7_7h4J1bSEzfcjIBakPi2cyVQ7y7jklLHsBeH1k,4648
importlib_metadata-8.0.0.dist-info/RECORD,, importlib_metadata-8.0.0.dist-info/RECORD,,
importlib_metadata-8.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 importlib_metadata-8.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
importlib_metadata-8.0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91 importlib_metadata-8.0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
importlib_metadata-8.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19 importlib_metadata-8.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
importlib_metadata/__init__.py,sha256=tZNB-23h8Bixi9uCrQqj9Yf0aeC--Josdy3IZRIQeB0,33798 importlib_metadata/__init__.py,sha256=tZNB-23h8Bixi9uCrQqj9Yf0aeC--Josdy3IZRIQeB0,33798
importlib_metadata/__pycache__/__init__.cpython-312.pyc,, importlib_metadata/__pycache__/__init__.cpython-312.pyc,,
importlib_metadata/__pycache__/_adapters.cpython-312.pyc,, importlib_metadata/__pycache__/_adapters.cpython-312.pyc,,
importlib_metadata/__pycache__/_collections.cpython-312.pyc,, importlib_metadata/__pycache__/_collections.cpython-312.pyc,,
importlib_metadata/__pycache__/_compat.cpython-312.pyc,, importlib_metadata/__pycache__/_compat.cpython-312.pyc,,
importlib_metadata/__pycache__/_functools.cpython-312.pyc,, importlib_metadata/__pycache__/_functools.cpython-312.pyc,,
importlib_metadata/__pycache__/_itertools.cpython-312.pyc,, importlib_metadata/__pycache__/_itertools.cpython-312.pyc,,
importlib_metadata/__pycache__/_meta.cpython-312.pyc,, importlib_metadata/__pycache__/_meta.cpython-312.pyc,,
importlib_metadata/__pycache__/_text.cpython-312.pyc,, importlib_metadata/__pycache__/_text.cpython-312.pyc,,
importlib_metadata/__pycache__/diagnose.cpython-312.pyc,, importlib_metadata/__pycache__/diagnose.cpython-312.pyc,,
importlib_metadata/_adapters.py,sha256=rIhWTwBvYA1bV7i-5FfVX38qEXDTXFeS5cb5xJtP3ks,2317 importlib_metadata/_adapters.py,sha256=rIhWTwBvYA1bV7i-5FfVX38qEXDTXFeS5cb5xJtP3ks,2317
importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743 importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
importlib_metadata/_compat.py,sha256=73QKrN9KNoaZzhbX5yPCCZa-FaALwXe8TPlDR72JgBU,1314 importlib_metadata/_compat.py,sha256=73QKrN9KNoaZzhbX5yPCCZa-FaALwXe8TPlDR72JgBU,1314
importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895 importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068 importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
importlib_metadata/_meta.py,sha256=nxZ7C8GVlcBFAKWyVOn_dn7ot_twBcbm1NmvjIetBHI,1801 importlib_metadata/_meta.py,sha256=nxZ7C8GVlcBFAKWyVOn_dn7ot_twBcbm1NmvjIetBHI,1801
importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166 importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
importlib_metadata/compat/__pycache__/__init__.cpython-312.pyc,, importlib_metadata/compat/__pycache__/__init__.cpython-312.pyc,,
importlib_metadata/compat/__pycache__/py311.cpython-312.pyc,, importlib_metadata/compat/__pycache__/py311.cpython-312.pyc,,
importlib_metadata/compat/__pycache__/py39.cpython-312.pyc,, importlib_metadata/compat/__pycache__/py39.cpython-312.pyc,,
importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608 importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
importlib_metadata/compat/py39.py,sha256=cPkMv6-0ilK-0Jw_Tkn0xYbOKJZc4WJKQHow0c2T44w,1102 importlib_metadata/compat/py39.py,sha256=cPkMv6-0ilK-0Jw_Tkn0xYbOKJZc4WJKQHow0c2T44w,1102
importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379 importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1,2 +1,2 @@
def check_build(): def check_build():
return return
@@ -1,6 +1,6 @@
py.extension_module( py.extension_module(
'_check_build', '_check_build',
cython_gen.process('_check_build.pyx'), cython_gen.process('_check_build.pyx'),
install: true, install: true,
subdir: 'sklearn/__check_build', subdir: 'sklearn/__check_build',
) )
@@ -1,116 +1,116 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Uses the pool adjacent violators algorithm (PAVA), with the # Uses the pool adjacent violators algorithm (PAVA), with the
# enhancement of searching for the longest decreasing subsequence to # enhancement of searching for the longest decreasing subsequence to
# pool at each step. # pool at each step.
import numpy as np import numpy as np
from cython cimport floating from cython cimport floating
def _inplace_contiguous_isotonic_regression(floating[::1] y, floating[::1] w): def _inplace_contiguous_isotonic_regression(floating[::1] y, floating[::1] w):
cdef: cdef:
Py_ssize_t n = y.shape[0], i, k Py_ssize_t n = y.shape[0], i, k
floating prev_y, sum_wy, sum_w floating prev_y, sum_wy, sum_w
Py_ssize_t[::1] target = np.arange(n, dtype=np.intp) Py_ssize_t[::1] target = np.arange(n, dtype=np.intp)
# target describes a list of blocks. At any time, if [i..j] (inclusive) is # target describes a list of blocks. At any time, if [i..j] (inclusive) is
# an active block, then target[i] := j and target[j] := i. # an active block, then target[i] := j and target[j] := i.
# For "active" indices (block starts): # For "active" indices (block starts):
# w[i] := sum{w_orig[j], j=[i..target[i]]} # w[i] := sum{w_orig[j], j=[i..target[i]]}
# y[i] := sum{y_orig[j]*w_orig[j], j=[i..target[i]]} / w[i] # y[i] := sum{y_orig[j]*w_orig[j], j=[i..target[i]]} / w[i]
with nogil: with nogil:
i = 0 i = 0
while i < n: while i < n:
k = target[i] + 1 k = target[i] + 1
if k == n: if k == n:
break break
if y[i] < y[k]: if y[i] < y[k]:
i = k i = k
continue continue
sum_wy = w[i] * y[i] sum_wy = w[i] * y[i]
sum_w = w[i] sum_w = w[i]
while True: while True:
# We are within a decreasing subsequence. # We are within a decreasing subsequence.
prev_y = y[k] prev_y = y[k]
sum_wy += w[k] * y[k] sum_wy += w[k] * y[k]
sum_w += w[k] sum_w += w[k]
k = target[k] + 1 k = target[k] + 1
if k == n or prev_y < y[k]: if k == n or prev_y < y[k]:
# Non-singleton decreasing subsequence is finished, # Non-singleton decreasing subsequence is finished,
# update first entry. # update first entry.
y[i] = sum_wy / sum_w y[i] = sum_wy / sum_w
w[i] = sum_w w[i] = sum_w
target[i] = k - 1 target[i] = k - 1
target[k - 1] = i target[k - 1] = i
if i > 0: if i > 0:
# Backtrack if we can. This makes the algorithm # Backtrack if we can. This makes the algorithm
# single-pass and ensures O(n) complexity. # single-pass and ensures O(n) complexity.
i = target[i - 1] i = target[i - 1]
# Otherwise, restart from the same point. # Otherwise, restart from the same point.
break break
# Reconstruct the solution. # Reconstruct the solution.
i = 0 i = 0
while i < n: while i < n:
k = target[i] + 1 k = target[i] + 1
y[i + 1 : k] = y[i] y[i + 1 : k] = y[i]
i = k i = k
def _make_unique(const floating[::1] X, def _make_unique(const floating[::1] X,
const floating[::1] y, const floating[::1] y,
const floating[::1] sample_weights): const floating[::1] sample_weights):
"""Average targets for duplicate X, drop duplicates. """Average targets for duplicate X, drop duplicates.
Aggregates duplicate X values into a single X value where Aggregates duplicate X values into a single X value where
the target y is a (sample_weighted) average of the individual the target y is a (sample_weighted) average of the individual
targets. targets.
Assumes that X is ordered, so that all duplicates follow each other. Assumes that X is ordered, so that all duplicates follow each other.
""" """
unique_values = len(np.unique(X)) unique_values = len(np.unique(X))
if floating is float: if floating is float:
dtype = np.float32 dtype = np.float32
else: else:
dtype = np.float64 dtype = np.float64
cdef floating[::1] y_out = np.empty(unique_values, dtype=dtype) cdef floating[::1] y_out = np.empty(unique_values, dtype=dtype)
cdef floating[::1] x_out = np.empty_like(y_out) cdef floating[::1] x_out = np.empty_like(y_out)
cdef floating[::1] weights_out = np.empty_like(y_out) cdef floating[::1] weights_out = np.empty_like(y_out)
cdef floating current_x = X[0] cdef floating current_x = X[0]
cdef floating current_y = 0 cdef floating current_y = 0
cdef floating current_weight = 0 cdef floating current_weight = 0
cdef int i = 0 cdef int i = 0
cdef int j cdef int j
cdef floating x cdef floating x
cdef int n_samples = len(X) cdef int n_samples = len(X)
cdef floating eps = np.finfo(dtype).resolution cdef floating eps = np.finfo(dtype).resolution
for j in range(n_samples): for j in range(n_samples):
x = X[j] x = X[j]
if x - current_x >= eps: if x - current_x >= eps:
# next unique value # next unique value
x_out[i] = current_x x_out[i] = current_x
weights_out[i] = current_weight weights_out[i] = current_weight
y_out[i] = current_y / current_weight y_out[i] = current_y / current_weight
i += 1 i += 1
current_x = x current_x = x
current_weight = sample_weights[j] current_weight = sample_weights[j]
current_y = y[j] * sample_weights[j] current_y = y[j] * sample_weights[j]
else: else:
current_weight += sample_weights[j] current_weight += sample_weights[j]
current_y += y[j] * sample_weights[j] current_y += y[j] * sample_weights[j]
x_out[i] = current_x x_out[i] = current_x
weights_out[i] = current_weight weights_out[i] = current_weight
y_out[i] = current_y / current_weight y_out[i] = current_y / current_weight
return( return(
np.asarray(x_out[:i+1]), np.asarray(x_out[:i+1]),
np.asarray(y_out[:i+1]), np.asarray(y_out[:i+1]),
np.asarray(weights_out[:i+1]), np.asarray(weights_out[:i+1]),
) )
@@ -1,101 +1,101 @@
# Fused types for input like y_true, raw_prediction, sample_weights. # Fused types for input like y_true, raw_prediction, sample_weights.
ctypedef fused floating_in: ctypedef fused floating_in:
double double
float float
# Fused types for output like gradient and hessian # Fused types for output like gradient and hessian
# We use a different fused types for input (floating_in) and output (floating_out), such # We use a different fused types for input (floating_in) and output (floating_out), such
# that input and output can have different dtypes in the same function call. A single # that input and output can have different dtypes in the same function call. A single
# fused type can only take on one single value (type) for all arguments in one function # fused type can only take on one single value (type) for all arguments in one function
# call. # call.
ctypedef fused floating_out: ctypedef fused floating_out:
double double
float float
# Struct to return 2 doubles # Struct to return 2 doubles
ctypedef struct double_pair: ctypedef struct double_pair:
double val1 double val1
double val2 double val2
# C base class for loss functions # C base class for loss functions
cdef class CyLossFunction: cdef class CyLossFunction:
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfSquaredError(CyLossFunction): cdef class CyHalfSquaredError(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyAbsoluteError(CyLossFunction): cdef class CyAbsoluteError(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyPinballLoss(CyLossFunction): cdef class CyPinballLoss(CyLossFunction):
cdef readonly double quantile # readonly makes it accessible from Python cdef readonly double quantile # readonly makes it accessible from Python
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHuberLoss(CyLossFunction): cdef class CyHuberLoss(CyLossFunction):
cdef public double delta # public makes it accessible from Python cdef public double delta # public makes it accessible from Python
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfPoissonLoss(CyLossFunction): cdef class CyHalfPoissonLoss(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfGammaLoss(CyLossFunction): cdef class CyHalfGammaLoss(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfTweedieLoss(CyLossFunction): cdef class CyHalfTweedieLoss(CyLossFunction):
cdef readonly double power # readonly makes it accessible from Python cdef readonly double power # readonly makes it accessible from Python
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfTweedieLossIdentity(CyLossFunction): cdef class CyHalfTweedieLossIdentity(CyLossFunction):
cdef readonly double power # readonly makes it accessible from Python cdef readonly double power # readonly makes it accessible from Python
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfBinomialLoss(CyLossFunction): cdef class CyHalfBinomialLoss(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyExponentialLoss(CyLossFunction): cdef class CyExponentialLoss(CyLossFunction):
cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_loss(self, double y_true, double raw_prediction) noexcept nogil
cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil cdef double cy_gradient(self, double y_true, double raw_prediction) noexcept nogil
cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil cdef double_pair cy_grad_hess(self, double y_true, double raw_prediction) noexcept nogil
cdef class CyHalfMultinomialLoss(): cdef class CyHalfMultinomialLoss():
cdef void cy_gradient( cdef void cy_gradient(
self, self,
const floating_in y_true, const floating_in y_true,
const floating_in[::1] raw_prediction, const floating_in[::1] raw_prediction,
const floating_in sample_weight, const floating_in sample_weight,
floating_out[::1] gradient_out, floating_out[::1] gradient_out,
) noexcept nogil ) noexcept nogil
File diff suppressed because it is too large Load Diff
@@ -1,23 +1,23 @@
# .pyx is generated, so this is needed to make Cython compilation work # .pyx is generated, so this is needed to make Cython compilation work
_loss_cython_tree = [ _loss_cython_tree = [
fs.copyfile('_loss.pxd') fs.copyfile('_loss.pxd')
] ]
_loss_pyx = custom_target( _loss_pyx = custom_target(
'_loss_pyx', '_loss_pyx',
output: '_loss.pyx', output: '_loss.pyx',
input: '_loss.pyx.tp', input: '_loss.pyx.tp',
command: [tempita, '@INPUT@', '-o', '@OUTDIR@'], command: [tempita, '@INPUT@', '-o', '@OUTDIR@'],
# TODO in principle this should go in py.exension_module below. This is # TODO in principle this should go in py.exension_module below. This is
# temporary work-around for dependency issue with .pyx.tp files. For more # temporary work-around for dependency issue with .pyx.tp files. For more
# details, see https://github.com/mesonbuild/meson/issues/13212 # details, see https://github.com/mesonbuild/meson/issues/13212
depends: _loss_cython_tree, depends: _loss_cython_tree,
) )
py.extension_module( py.extension_module(
'_loss', '_loss',
cython_gen.process(_loss_pyx), cython_gen.process(_loss_pyx),
dependencies: [openmp_dep], dependencies: [openmp_dep],
install: true, install: true,
subdir: 'sklearn/_loss', subdir: 'sklearn/_loss',
) )
@@ -1,41 +1,41 @@
# Fast inner loop for DBSCAN. # Fast inner loop for DBSCAN.
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from libcpp.vector cimport vector from libcpp.vector cimport vector
from ..utils._typedefs cimport uint8_t, intp_t from ..utils._typedefs cimport uint8_t, intp_t
def dbscan_inner(const uint8_t[::1] is_core, def dbscan_inner(const uint8_t[::1] is_core,
object[:] neighborhoods, object[:] neighborhoods,
intp_t[::1] labels): intp_t[::1] labels):
cdef intp_t i, label_num = 0, v cdef intp_t i, label_num = 0, v
cdef intp_t[:] neighb cdef intp_t[:] neighb
cdef vector[intp_t] stack cdef vector[intp_t] stack
for i in range(labels.shape[0]): for i in range(labels.shape[0]):
if labels[i] != -1 or not is_core[i]: if labels[i] != -1 or not is_core[i]:
continue continue
# Depth-first search starting from i, ending at the non-core points. # Depth-first search starting from i, ending at the non-core points.
# This is very similar to the classic algorithm for computing connected # This is very similar to the classic algorithm for computing connected
# components, the difference being that we label non-core points as # components, the difference being that we label non-core points as
# part of a cluster (component), but don't expand their neighborhoods. # part of a cluster (component), but don't expand their neighborhoods.
while True: while True:
if labels[i] == -1: if labels[i] == -1:
labels[i] = label_num labels[i] = label_num
if is_core[i]: if is_core[i]:
neighb = neighborhoods[i] neighb = neighborhoods[i]
for i in range(neighb.shape[0]): for i in range(neighb.shape[0]):
v = neighb[i] v = neighb[i]
if labels[v] == -1: if labels[v] == -1:
stack.push_back(v) stack.push_back(v)
if stack.size() == 0: if stack.size() == 0:
break break
i = stack.back() i = stack.back()
stack.pop_back() stack.pop_back()
label_num += 1 label_num += 1
@@ -1,274 +1,274 @@
# Minimum spanning tree single linkage implementation for hdbscan # Minimum spanning tree single linkage implementation for hdbscan
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, # 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice, # 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its contributors # 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without # may be used to endorse or promote products derived from this software without
# specific prior written permission. # specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
cimport numpy as cnp cimport numpy as cnp
from libc.float cimport DBL_MAX from libc.float cimport DBL_MAX
import numpy as np import numpy as np
from ...metrics._dist_metrics cimport DistanceMetric64 from ...metrics._dist_metrics cimport DistanceMetric64
from ...cluster._hierarchical_fast cimport UnionFind from ...cluster._hierarchical_fast cimport UnionFind
from ...cluster._hdbscan._tree cimport HIERARCHY_t from ...cluster._hdbscan._tree cimport HIERARCHY_t
from ...cluster._hdbscan._tree import HIERARCHY_dtype from ...cluster._hdbscan._tree import HIERARCHY_dtype
from ...utils._typedefs cimport intp_t, float64_t, int64_t, uint8_t from ...utils._typedefs cimport intp_t, float64_t, int64_t, uint8_t
cnp.import_array() cnp.import_array()
cdef extern from "numpy/arrayobject.h": cdef extern from "numpy/arrayobject.h":
intp_t * PyArray_SHAPE(cnp.PyArrayObject *) intp_t * PyArray_SHAPE(cnp.PyArrayObject *)
# Numpy structured dtype representing a single ordered edge in Prim's algorithm # Numpy structured dtype representing a single ordered edge in Prim's algorithm
MST_edge_dtype = np.dtype([ MST_edge_dtype = np.dtype([
("current_node", np.int64), ("current_node", np.int64),
("next_node", np.int64), ("next_node", np.int64),
("distance", np.float64), ("distance", np.float64),
]) ])
# Packed shouldn't make a difference since they're all 8-byte quantities, # Packed shouldn't make a difference since they're all 8-byte quantities,
# but it's included just to be safe. # but it's included just to be safe.
ctypedef packed struct MST_edge_t: ctypedef packed struct MST_edge_t:
int64_t current_node int64_t current_node
int64_t next_node int64_t next_node
float64_t distance float64_t distance
cpdef cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst_from_mutual_reachability( cpdef cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst_from_mutual_reachability(
cnp.ndarray[float64_t, ndim=2] mutual_reachability cnp.ndarray[float64_t, ndim=2] mutual_reachability
): ):
"""Compute the Minimum Spanning Tree (MST) representation of the mutual- """Compute the Minimum Spanning Tree (MST) representation of the mutual-
reachability graph using Prim's algorithm. reachability graph using Prim's algorithm.
Parameters Parameters
---------- ----------
mutual_reachability : ndarray of shape (n_samples, n_samples) mutual_reachability : ndarray of shape (n_samples, n_samples)
Array of mutual-reachabilities between samples. Array of mutual-reachabilities between samples.
Returns Returns
------- -------
mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype
The MST representation of the mutual-reachability graph. The MST is The MST representation of the mutual-reachability graph. The MST is
represented as a collection of edges. represented as a collection of edges.
""" """
cdef: cdef:
# Note: we utilize ndarray's over memory-views to make use of numpy # Note: we utilize ndarray's over memory-views to make use of numpy
# binary indexing and sub-selection below. # binary indexing and sub-selection below.
cnp.ndarray[int64_t, ndim=1, mode='c'] current_labels cnp.ndarray[int64_t, ndim=1, mode='c'] current_labels
cnp.ndarray[float64_t, ndim=1, mode='c'] min_reachability, left, right cnp.ndarray[float64_t, ndim=1, mode='c'] min_reachability, left, right
cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst
cnp.ndarray[uint8_t, mode='c'] label_filter cnp.ndarray[uint8_t, mode='c'] label_filter
int64_t n_samples = PyArray_SHAPE(<cnp.PyArrayObject*> mutual_reachability)[0] int64_t n_samples = PyArray_SHAPE(<cnp.PyArrayObject*> mutual_reachability)[0]
int64_t current_node, new_node_index, new_node, i int64_t current_node, new_node_index, new_node, i
mst = np.empty(n_samples - 1, dtype=MST_edge_dtype) mst = np.empty(n_samples - 1, dtype=MST_edge_dtype)
current_labels = np.arange(n_samples, dtype=np.int64) current_labels = np.arange(n_samples, dtype=np.int64)
current_node = 0 current_node = 0
min_reachability = np.full(n_samples, fill_value=np.inf, dtype=np.float64) min_reachability = np.full(n_samples, fill_value=np.inf, dtype=np.float64)
for i in range(0, n_samples - 1): for i in range(0, n_samples - 1):
label_filter = current_labels != current_node label_filter = current_labels != current_node
current_labels = current_labels[label_filter] current_labels = current_labels[label_filter]
left = min_reachability[label_filter] left = min_reachability[label_filter]
right = mutual_reachability[current_node][current_labels] right = mutual_reachability[current_node][current_labels]
min_reachability = np.minimum(left, right) min_reachability = np.minimum(left, right)
new_node_index = np.argmin(min_reachability) new_node_index = np.argmin(min_reachability)
new_node = current_labels[new_node_index] new_node = current_labels[new_node_index]
mst[i].current_node = current_node mst[i].current_node = current_node
mst[i].next_node = new_node mst[i].next_node = new_node
mst[i].distance = min_reachability[new_node_index] mst[i].distance = min_reachability[new_node_index]
current_node = new_node current_node = new_node
return mst return mst
cpdef cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst_from_data_matrix( cpdef cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst_from_data_matrix(
const float64_t[:, ::1] raw_data, const float64_t[:, ::1] raw_data,
const float64_t[::1] core_distances, const float64_t[::1] core_distances,
DistanceMetric64 dist_metric, DistanceMetric64 dist_metric,
float64_t alpha=1.0 float64_t alpha=1.0
): ):
"""Compute the Minimum Spanning Tree (MST) representation of the mutual- """Compute the Minimum Spanning Tree (MST) representation of the mutual-
reachability graph generated from the provided `raw_data` and reachability graph generated from the provided `raw_data` and
`core_distances` using Prim's algorithm. `core_distances` using Prim's algorithm.
Parameters Parameters
---------- ----------
raw_data : ndarray of shape (n_samples, n_features) raw_data : ndarray of shape (n_samples, n_features)
Input array of data samples. Input array of data samples.
core_distances : ndarray of shape (n_samples,) core_distances : ndarray of shape (n_samples,)
An array containing the core-distance calculated for each corresponding An array containing the core-distance calculated for each corresponding
sample. sample.
dist_metric : DistanceMetric dist_metric : DistanceMetric
The distance metric to use when calculating pairwise distances for The distance metric to use when calculating pairwise distances for
determining mutual-reachability. determining mutual-reachability.
Returns Returns
------- -------
mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype
The MST representation of the mutual-reachability graph. The MST is The MST representation of the mutual-reachability graph. The MST is
represented as a collection of edges. represented as a collection of edges.
""" """
cdef: cdef:
uint8_t[::1] in_tree uint8_t[::1] in_tree
float64_t[::1] min_reachability float64_t[::1] min_reachability
int64_t[::1] current_sources int64_t[::1] current_sources
cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst cnp.ndarray[MST_edge_t, ndim=1, mode='c'] mst
int64_t current_node, source_node, new_node, next_node_source int64_t current_node, source_node, new_node, next_node_source
int64_t i, j, n_samples, num_features int64_t i, j, n_samples, num_features
float64_t current_node_core_dist, new_reachability, mutual_reachability_distance float64_t current_node_core_dist, new_reachability, mutual_reachability_distance
float64_t next_node_min_reach, pair_distance, next_node_core_dist float64_t next_node_min_reach, pair_distance, next_node_core_dist
n_samples = raw_data.shape[0] n_samples = raw_data.shape[0]
num_features = raw_data.shape[1] num_features = raw_data.shape[1]
mst = np.empty(n_samples - 1, dtype=MST_edge_dtype) mst = np.empty(n_samples - 1, dtype=MST_edge_dtype)
in_tree = np.zeros(n_samples, dtype=np.uint8) in_tree = np.zeros(n_samples, dtype=np.uint8)
min_reachability = np.full(n_samples, fill_value=np.inf, dtype=np.float64) min_reachability = np.full(n_samples, fill_value=np.inf, dtype=np.float64)
current_sources = np.ones(n_samples, dtype=np.int64) current_sources = np.ones(n_samples, dtype=np.int64)
current_node = 0 current_node = 0
# The following loop dynamically updates minimum reachability node-by-node, # The following loop dynamically updates minimum reachability node-by-node,
# avoiding unnecessary computation where possible. # avoiding unnecessary computation where possible.
for i in range(0, n_samples - 1): for i in range(0, n_samples - 1):
in_tree[current_node] = 1 in_tree[current_node] = 1
current_node_core_dist = core_distances[current_node] current_node_core_dist = core_distances[current_node]
new_reachability = DBL_MAX new_reachability = DBL_MAX
source_node = 0 source_node = 0
new_node = 0 new_node = 0
for j in range(n_samples): for j in range(n_samples):
if in_tree[j]: if in_tree[j]:
continue continue
next_node_min_reach = min_reachability[j] next_node_min_reach = min_reachability[j]
next_node_source = current_sources[j] next_node_source = current_sources[j]
pair_distance = dist_metric.dist( pair_distance = dist_metric.dist(
&raw_data[current_node, 0], &raw_data[current_node, 0],
&raw_data[j, 0], &raw_data[j, 0],
num_features num_features
) )
pair_distance /= alpha pair_distance /= alpha
next_node_core_dist = core_distances[j] next_node_core_dist = core_distances[j]
mutual_reachability_distance = max( mutual_reachability_distance = max(
current_node_core_dist, current_node_core_dist,
next_node_core_dist, next_node_core_dist,
pair_distance pair_distance
) )
# If MRD(i, j) is smaller than node j's min_reachability, we update # If MRD(i, j) is smaller than node j's min_reachability, we update
# node j's min_reachability for future reference. # node j's min_reachability for future reference.
if mutual_reachability_distance < next_node_min_reach: if mutual_reachability_distance < next_node_min_reach:
min_reachability[j] = mutual_reachability_distance min_reachability[j] = mutual_reachability_distance
current_sources[j] = current_node current_sources[j] = current_node
# If MRD(i, j) is also smaller than node i's current # If MRD(i, j) is also smaller than node i's current
# min_reachability, we update and set their edge as the current # min_reachability, we update and set their edge as the current
# MST edge candidate. # MST edge candidate.
if mutual_reachability_distance < new_reachability: if mutual_reachability_distance < new_reachability:
new_reachability = mutual_reachability_distance new_reachability = mutual_reachability_distance
source_node = current_node source_node = current_node
new_node = j new_node = j
# If the node j is closer to another node already in the tree, we # If the node j is closer to another node already in the tree, we
# make their edge the current MST candidate edge. # make their edge the current MST candidate edge.
elif next_node_min_reach < new_reachability: elif next_node_min_reach < new_reachability:
new_reachability = next_node_min_reach new_reachability = next_node_min_reach
source_node = next_node_source source_node = next_node_source
new_node = j new_node = j
mst[i].current_node = source_node mst[i].current_node = source_node
mst[i].next_node = new_node mst[i].next_node = new_node
mst[i].distance = new_reachability mst[i].distance = new_reachability
current_node = new_node current_node = new_node
return mst return mst
cpdef cnp.ndarray[HIERARCHY_t, ndim=1, mode="c"] make_single_linkage(const MST_edge_t[::1] mst): cpdef cnp.ndarray[HIERARCHY_t, ndim=1, mode="c"] make_single_linkage(const MST_edge_t[::1] mst):
"""Construct a single-linkage tree from an MST. """Construct a single-linkage tree from an MST.
Parameters Parameters
---------- ----------
mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype mst : ndarray of shape (n_samples - 1,), dtype=MST_edge_dtype
The MST representation of the mutual-reachability graph. The MST is The MST representation of the mutual-reachability graph. The MST is
represented as a collection of edges. represented as a collection of edges.
Returns Returns
------- -------
single_linkage : ndarray of shape (n_samples - 1,), dtype=HIERARCHY_dtype single_linkage : ndarray of shape (n_samples - 1,), dtype=HIERARCHY_dtype
The single-linkage tree tree (dendrogram) built from the MST. Each The single-linkage tree tree (dendrogram) built from the MST. Each
of the array represents the following: of the array represents the following:
- left node/cluster - left node/cluster
- right node/cluster - right node/cluster
- distance - distance
- new cluster size - new cluster size
""" """
cdef: cdef:
cnp.ndarray[HIERARCHY_t, ndim=1, mode="c"] single_linkage cnp.ndarray[HIERARCHY_t, ndim=1, mode="c"] single_linkage
# Note mst.shape[0] is one fewer than the number of samples # Note mst.shape[0] is one fewer than the number of samples
int64_t n_samples = mst.shape[0] + 1 int64_t n_samples = mst.shape[0] + 1
intp_t current_node_cluster, next_node_cluster intp_t current_node_cluster, next_node_cluster
int64_t current_node, next_node, i int64_t current_node, next_node, i
float64_t distance float64_t distance
UnionFind U = UnionFind(n_samples) UnionFind U = UnionFind(n_samples)
single_linkage = np.zeros(n_samples - 1, dtype=HIERARCHY_dtype) single_linkage = np.zeros(n_samples - 1, dtype=HIERARCHY_dtype)
for i in range(n_samples - 1): for i in range(n_samples - 1):
current_node = mst[i].current_node current_node = mst[i].current_node
next_node = mst[i].next_node next_node = mst[i].next_node
distance = mst[i].distance distance = mst[i].distance
current_node_cluster = U.fast_find(current_node) current_node_cluster = U.fast_find(current_node)
next_node_cluster = U.fast_find(next_node) next_node_cluster = U.fast_find(next_node)
single_linkage[i].left_node = current_node_cluster single_linkage[i].left_node = current_node_cluster
single_linkage[i].right_node = next_node_cluster single_linkage[i].right_node = next_node_cluster
single_linkage[i].value = distance single_linkage[i].value = distance
single_linkage[i].cluster_size = U.size[current_node_cluster] + U.size[next_node_cluster] single_linkage[i].cluster_size = U.size[current_node_cluster] + U.size[next_node_cluster]
U.union(current_node_cluster, next_node_cluster) U.union(current_node_cluster, next_node_cluster)
return single_linkage return single_linkage
@@ -1,210 +1,210 @@
# mutual reachability distance computations # mutual reachability distance computations
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, # 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice, # 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its contributors # 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without # may be used to endorse or promote products derived from this software without
# specific prior written permission. # specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
cimport numpy as cnp cimport numpy as cnp
import numpy as np import numpy as np
from scipy.sparse import issparse from scipy.sparse import issparse
from cython cimport floating, integral from cython cimport floating, integral
from libc.math cimport isfinite, INFINITY from libc.math cimport isfinite, INFINITY
from ...utils._typedefs cimport intp_t from ...utils._typedefs cimport intp_t
cnp.import_array() cnp.import_array()
def mutual_reachability_graph( def mutual_reachability_graph(
distance_matrix, min_samples=5, max_distance=0.0 distance_matrix, min_samples=5, max_distance=0.0
): ):
"""Compute the weighted adjacency matrix of the mutual reachability graph. """Compute the weighted adjacency matrix of the mutual reachability graph.
The mutual reachability distance used to build the graph is defined as:: The mutual reachability distance used to build the graph is defined as::
max(d_core(x_p), d_core(x_q), d(x_p, x_q)) max(d_core(x_p), d_core(x_q), d(x_p, x_q))
and the core distance `d_core` is defined as the distance between a point and the core distance `d_core` is defined as the distance between a point
`x_p` and its k-th nearest neighbor. `x_p` and its k-th nearest neighbor.
Note that all computations are done in-place. Note that all computations are done in-place.
Parameters Parameters
---------- ----------
distance_matrix : {ndarray, sparse matrix} of shape (n_samples, n_samples) distance_matrix : {ndarray, sparse matrix} of shape (n_samples, n_samples)
Array of distances between samples. If sparse, the array must be in Array of distances between samples. If sparse, the array must be in
`CSR` format. `CSR` format.
min_samples : int, default=5 min_samples : int, default=5
The parameter `k` used to calculate the distance between a point The parameter `k` used to calculate the distance between a point
`x_p` and its k-th nearest neighbor. `x_p` and its k-th nearest neighbor.
max_distance : float, default=0.0 max_distance : float, default=0.0
The distance which `np.inf` is replaced with. When the true mutual- The distance which `np.inf` is replaced with. When the true mutual-
reachability distance is measured to be infinite, it is instead reachability distance is measured to be infinite, it is instead
truncated to `max_dist`. Only used when `distance_matrix` is a sparse truncated to `max_dist`. Only used when `distance_matrix` is a sparse
matrix. matrix.
Returns Returns
------- -------
mututal_reachability_graph: {ndarray, sparse matrix} of shape \ mututal_reachability_graph: {ndarray, sparse matrix} of shape \
(n_samples, n_samples) (n_samples, n_samples)
Weighted adjacency matrix of the mutual reachability graph. Weighted adjacency matrix of the mutual reachability graph.
References References
---------- ----------
.. [1] Campello, R. J., Moulavi, D., & Sander, J. (2013, April). .. [1] Campello, R. J., Moulavi, D., & Sander, J. (2013, April).
Density-based clustering based on hierarchical density estimates. Density-based clustering based on hierarchical density estimates.
In Pacific-Asia Conference on Knowledge Discovery and Data Mining In Pacific-Asia Conference on Knowledge Discovery and Data Mining
(pp. 160-172). Springer Berlin Heidelberg. (pp. 160-172). Springer Berlin Heidelberg.
""" """
further_neighbor_idx = min_samples - 1 further_neighbor_idx = min_samples - 1
if issparse(distance_matrix): if issparse(distance_matrix):
if distance_matrix.format != "csr": if distance_matrix.format != "csr":
raise ValueError( raise ValueError(
"Only sparse CSR matrices are supported for `distance_matrix`." "Only sparse CSR matrices are supported for `distance_matrix`."
) )
_sparse_mutual_reachability_graph( _sparse_mutual_reachability_graph(
distance_matrix.data, distance_matrix.data,
distance_matrix.indices, distance_matrix.indices,
distance_matrix.indptr, distance_matrix.indptr,
distance_matrix.shape[0], distance_matrix.shape[0],
further_neighbor_idx=further_neighbor_idx, further_neighbor_idx=further_neighbor_idx,
max_distance=max_distance, max_distance=max_distance,
) )
else: else:
_dense_mutual_reachability_graph( _dense_mutual_reachability_graph(
distance_matrix, further_neighbor_idx=further_neighbor_idx distance_matrix, further_neighbor_idx=further_neighbor_idx
) )
return distance_matrix return distance_matrix
def _dense_mutual_reachability_graph( def _dense_mutual_reachability_graph(
floating[:, :] distance_matrix, floating[:, :] distance_matrix,
intp_t further_neighbor_idx, intp_t further_neighbor_idx,
): ):
"""Dense implementation of mutual reachability graph. """Dense implementation of mutual reachability graph.
The computation is done in-place, i.e. the distance matrix is modified The computation is done in-place, i.e. the distance matrix is modified
directly. directly.
Parameters Parameters
---------- ----------
distance_matrix : ndarray of shape (n_samples, n_samples) distance_matrix : ndarray of shape (n_samples, n_samples)
Array of distances between samples. Array of distances between samples.
further_neighbor_idx : int further_neighbor_idx : int
The index of the furthest neighbor to use to define the core distances. The index of the furthest neighbor to use to define the core distances.
""" """
cdef: cdef:
intp_t i, j, n_samples = distance_matrix.shape[0] intp_t i, j, n_samples = distance_matrix.shape[0]
floating mutual_reachability_distance floating mutual_reachability_distance
floating[::1] core_distances floating[::1] core_distances
# We assume that the distance matrix is symmetric. We choose to sort every # We assume that the distance matrix is symmetric. We choose to sort every
# row to have the same implementation than the sparse case that requires # row to have the same implementation than the sparse case that requires
# CSR matrix. # CSR matrix.
core_distances = np.ascontiguousarray( core_distances = np.ascontiguousarray(
np.partition( np.partition(
distance_matrix, further_neighbor_idx, axis=1 distance_matrix, further_neighbor_idx, axis=1
)[:, further_neighbor_idx] )[:, further_neighbor_idx]
) )
with nogil: with nogil:
# TODO: Update w/ prange with thread count based on # TODO: Update w/ prange with thread count based on
# _openmp_effective_n_threads # _openmp_effective_n_threads
for i in range(n_samples): for i in range(n_samples):
for j in range(n_samples): for j in range(n_samples):
mutual_reachability_distance = max( mutual_reachability_distance = max(
core_distances[i], core_distances[i],
core_distances[j], core_distances[j],
distance_matrix[i, j], distance_matrix[i, j],
) )
distance_matrix[i, j] = mutual_reachability_distance distance_matrix[i, j] = mutual_reachability_distance
def _sparse_mutual_reachability_graph( def _sparse_mutual_reachability_graph(
cnp.ndarray[floating, ndim=1, mode="c"] data, cnp.ndarray[floating, ndim=1, mode="c"] data,
cnp.ndarray[integral, ndim=1, mode="c"] indices, cnp.ndarray[integral, ndim=1, mode="c"] indices,
cnp.ndarray[integral, ndim=1, mode="c"] indptr, cnp.ndarray[integral, ndim=1, mode="c"] indptr,
intp_t n_samples, intp_t n_samples,
intp_t further_neighbor_idx, intp_t further_neighbor_idx,
floating max_distance, floating max_distance,
): ):
"""Sparse implementation of mutual reachability graph. """Sparse implementation of mutual reachability graph.
The computation is done in-place, i.e. the distance matrix is modified The computation is done in-place, i.e. the distance matrix is modified
directly. This implementation only accepts `CSR` format sparse matrices. directly. This implementation only accepts `CSR` format sparse matrices.
Parameters Parameters
---------- ----------
distance_matrix : sparse matrix of shape (n_samples, n_samples) distance_matrix : sparse matrix of shape (n_samples, n_samples)
Sparse matrix of distances between samples. The sparse format should Sparse matrix of distances between samples. The sparse format should
be `CSR`. be `CSR`.
further_neighbor_idx : int further_neighbor_idx : int
The index of the furthest neighbor to use to define the core distances. The index of the furthest neighbor to use to define the core distances.
max_distance : float max_distance : float
The distance which `np.inf` is replaced with. When the true mutual- The distance which `np.inf` is replaced with. When the true mutual-
reachability distance is measured to be infinite, it is instead reachability distance is measured to be infinite, it is instead
truncated to `max_dist`. Only used when `distance_matrix` is a sparse truncated to `max_dist`. Only used when `distance_matrix` is a sparse
matrix. matrix.
""" """
cdef: cdef:
integral i, col_ind, row_ind integral i, col_ind, row_ind
floating mutual_reachability_distance floating mutual_reachability_distance
floating[:] core_distances floating[:] core_distances
floating[:] row_data floating[:] row_data
if floating is float: if floating is float:
dtype = np.float32 dtype = np.float32
else: else:
dtype = np.float64 dtype = np.float64
core_distances = np.empty(n_samples, dtype=dtype) core_distances = np.empty(n_samples, dtype=dtype)
for i in range(n_samples): for i in range(n_samples):
row_data = data[indptr[i]:indptr[i + 1]] row_data = data[indptr[i]:indptr[i + 1]]
if further_neighbor_idx < row_data.size: if further_neighbor_idx < row_data.size:
core_distances[i] = np.partition( core_distances[i] = np.partition(
row_data, further_neighbor_idx row_data, further_neighbor_idx
)[further_neighbor_idx] )[further_neighbor_idx]
else: else:
core_distances[i] = INFINITY core_distances[i] = INFINITY
with nogil: with nogil:
for row_ind in range(n_samples): for row_ind in range(n_samples):
for i in range(indptr[row_ind], indptr[row_ind + 1]): for i in range(indptr[row_ind], indptr[row_ind + 1]):
col_ind = indices[i] col_ind = indices[i]
mutual_reachability_distance = max( mutual_reachability_distance = max(
core_distances[row_ind], core_distances[col_ind], data[i] core_distances[row_ind], core_distances[col_ind], data[i]
) )
if isfinite(mutual_reachability_distance): if isfinite(mutual_reachability_distance):
data[i] = mutual_reachability_distance data[i] = mutual_reachability_distance
elif max_distance > 0: elif max_distance > 0:
data[i] = max_distance data[i] = max_distance
@@ -1,49 +1,49 @@
# Copyright (c) 2015, Leland McInnes # Copyright (c) 2015, Leland McInnes
# All rights reserved. # All rights reserved.
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice, # 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. # this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice, # 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its contributors # 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without # may be used to endorse or promote products derived from this software without
# specific prior written permission. # specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from ...utils._typedefs cimport intp_t, float64_t, uint8_t from ...utils._typedefs cimport intp_t, float64_t, uint8_t
cimport numpy as cnp cimport numpy as cnp
# This corresponds to the scipy.cluster.hierarchy format # This corresponds to the scipy.cluster.hierarchy format
ctypedef packed struct HIERARCHY_t: ctypedef packed struct HIERARCHY_t:
intp_t left_node intp_t left_node
intp_t right_node intp_t right_node
float64_t value float64_t value
intp_t cluster_size intp_t cluster_size
# Effectively an edgelist encoding a parent/child pair, along with a value and # Effectively an edgelist encoding a parent/child pair, along with a value and
# the corresponding cluster_size in each row providing a tree structure. # the corresponding cluster_size in each row providing a tree structure.
ctypedef packed struct CONDENSED_t: ctypedef packed struct CONDENSED_t:
intp_t parent intp_t parent
intp_t child intp_t child
float64_t value float64_t value
intp_t cluster_size intp_t cluster_size
cdef extern from "numpy/arrayobject.h": cdef extern from "numpy/arrayobject.h":
intp_t * PyArray_SHAPE(cnp.PyArrayObject *) intp_t * PyArray_SHAPE(cnp.PyArrayObject *)
File diff suppressed because it is too large Load Diff
@@ -1,15 +1,15 @@
cluster_hdbscan_extension_metadata = { cluster_hdbscan_extension_metadata = {
'_linkage': {'sources': [cython_gen.process('_linkage.pyx'), metrics_cython_tree]}, '_linkage': {'sources': [cython_gen.process('_linkage.pyx'), metrics_cython_tree]},
'_reachability': {'sources': [cython_gen.process('_reachability.pyx')]}, '_reachability': {'sources': [cython_gen.process('_reachability.pyx')]},
'_tree': {'sources': [cython_gen.process('_tree.pyx')]} '_tree': {'sources': [cython_gen.process('_tree.pyx')]}
} }
foreach ext_name, ext_dict : cluster_hdbscan_extension_metadata foreach ext_name, ext_dict : cluster_hdbscan_extension_metadata
py.extension_module( py.extension_module(
ext_name, ext_name,
ext_dict.get('sources'), ext_dict.get('sources'),
dependencies: [np_dep], dependencies: [np_dep],
subdir: 'sklearn/cluster/_hdbscan', subdir: 'sklearn/cluster/_hdbscan',
install: true install: true
) )
endforeach endforeach
@@ -1,9 +1,9 @@
from ..utils._typedefs cimport intp_t from ..utils._typedefs cimport intp_t
cdef class UnionFind: cdef class UnionFind:
cdef intp_t next_label cdef intp_t next_label
cdef intp_t[:] parent cdef intp_t[:] parent
cdef intp_t[:] size cdef intp_t[:] size
cdef void union(self, intp_t m, intp_t n) noexcept cdef void union(self, intp_t m, intp_t n) noexcept
cdef intp_t fast_find(self, intp_t n) noexcept cdef intp_t fast_find(self, intp_t n) noexcept
File diff suppressed because it is too large Load Diff
@@ -1,48 +1,48 @@
from cython cimport floating from cython cimport floating
cdef floating _euclidean_dense_dense( cdef floating _euclidean_dense_dense(
const floating*, const floating*,
const floating*, const floating*,
int, int,
bint bint
) noexcept nogil ) noexcept nogil
cdef floating _euclidean_sparse_dense( cdef floating _euclidean_sparse_dense(
const floating[::1], const floating[::1],
const int[::1], const int[::1],
const floating[::1], const floating[::1],
floating, floating,
bint bint
) noexcept nogil ) noexcept nogil
cpdef void _relocate_empty_clusters_dense( cpdef void _relocate_empty_clusters_dense(
const floating[:, ::1], const floating[:, ::1],
const floating[::1], const floating[::1],
const floating[:, ::1], const floating[:, ::1],
floating[:, ::1], floating[:, ::1],
floating[::1], floating[::1],
const int[::1] const int[::1]
) )
cpdef void _relocate_empty_clusters_sparse( cpdef void _relocate_empty_clusters_sparse(
const floating[::1], const floating[::1],
const int[::1], const int[::1],
const int[::1], const int[::1],
const floating[::1], const floating[::1],
const floating[:, ::1], const floating[:, ::1],
floating[:, ::1], floating[:, ::1],
floating[::1], floating[::1],
const int[::1] const int[::1]
) )
cdef void _average_centers( cdef void _average_centers(
floating[:, ::1], floating[:, ::1],
const floating[::1] const floating[::1]
) )
cdef void _center_shift( cdef void _center_shift(
const floating[:, ::1], const floating[:, ::1],
const floating[:, ::1], const floating[:, ::1],
floating[::1] floating[::1]
) )
@@ -1,328 +1,328 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
import numpy as np import numpy as np
from cython cimport floating from cython cimport floating
from cython.parallel cimport prange from cython.parallel cimport prange
from libc.math cimport sqrt from libc.math cimport sqrt
from ..utils.extmath import row_norms from ..utils.extmath import row_norms
# Number of samples per data chunk defined as a global constant. # Number of samples per data chunk defined as a global constant.
CHUNK_SIZE = 256 CHUNK_SIZE = 256
cdef floating _euclidean_dense_dense( cdef floating _euclidean_dense_dense(
const floating* a, # IN const floating* a, # IN
const floating* b, # IN const floating* b, # IN
int n_features, int n_features,
bint squared bint squared
) noexcept nogil: ) noexcept nogil:
"""Euclidean distance between a dense and b dense""" """Euclidean distance between a dense and b dense"""
cdef: cdef:
int i int i
int n = n_features // 4 int n = n_features // 4
int rem = n_features % 4 int rem = n_features % 4
floating result = 0 floating result = 0
# We manually unroll the loop for better cache optimization. # We manually unroll the loop for better cache optimization.
for i in range(n): for i in range(n):
result += ( result += (
(a[0] - b[0]) * (a[0] - b[0]) + (a[0] - b[0]) * (a[0] - b[0]) +
(a[1] - b[1]) * (a[1] - b[1]) + (a[1] - b[1]) * (a[1] - b[1]) +
(a[2] - b[2]) * (a[2] - b[2]) + (a[2] - b[2]) * (a[2] - b[2]) +
(a[3] - b[3]) * (a[3] - b[3]) (a[3] - b[3]) * (a[3] - b[3])
) )
a += 4 a += 4
b += 4 b += 4
for i in range(rem): for i in range(rem):
result += (a[i] - b[i]) * (a[i] - b[i]) result += (a[i] - b[i]) * (a[i] - b[i])
return result if squared else sqrt(result) return result if squared else sqrt(result)
def _euclidean_dense_dense_wrapper( def _euclidean_dense_dense_wrapper(
const floating[::1] a, const floating[::1] a,
const floating[::1] b, const floating[::1] b,
bint squared bint squared
): ):
"""Wrapper of _euclidean_dense_dense for testing purpose""" """Wrapper of _euclidean_dense_dense for testing purpose"""
return _euclidean_dense_dense(&a[0], &b[0], a.shape[0], squared) return _euclidean_dense_dense(&a[0], &b[0], a.shape[0], squared)
cdef floating _euclidean_sparse_dense( cdef floating _euclidean_sparse_dense(
const floating[::1] a_data, # IN const floating[::1] a_data, # IN
const int[::1] a_indices, # IN const int[::1] a_indices, # IN
const floating[::1] b, # IN const floating[::1] b, # IN
floating b_squared_norm, floating b_squared_norm,
bint squared bint squared
) noexcept nogil: ) noexcept nogil:
"""Euclidean distance between a sparse and b dense""" """Euclidean distance between a sparse and b dense"""
cdef: cdef:
int nnz = a_indices.shape[0] int nnz = a_indices.shape[0]
int i int i
floating tmp, bi floating tmp, bi
floating result = 0.0 floating result = 0.0
for i in range(nnz): for i in range(nnz):
bi = b[a_indices[i]] bi = b[a_indices[i]]
tmp = a_data[i] - bi tmp = a_data[i] - bi
result += tmp * tmp - bi * bi result += tmp * tmp - bi * bi
result += b_squared_norm result += b_squared_norm
if result < 0: if result < 0:
result = 0.0 result = 0.0
return result if squared else sqrt(result) return result if squared else sqrt(result)
def _euclidean_sparse_dense_wrapper( def _euclidean_sparse_dense_wrapper(
const floating[::1] a_data, const floating[::1] a_data,
const int[::1] a_indices, const int[::1] a_indices,
const floating[::1] b, const floating[::1] b,
floating b_squared_norm, floating b_squared_norm,
bint squared bint squared
): ):
"""Wrapper of _euclidean_sparse_dense for testing purpose""" """Wrapper of _euclidean_sparse_dense for testing purpose"""
return _euclidean_sparse_dense( return _euclidean_sparse_dense(
a_data, a_indices, b, b_squared_norm, squared) a_data, a_indices, b, b_squared_norm, squared)
cpdef floating _inertia_dense( cpdef floating _inertia_dense(
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers, # IN const floating[:, ::1] centers, # IN
const int[::1] labels, # IN const int[::1] labels, # IN
int n_threads, int n_threads,
int single_label=-1, int single_label=-1,
): ):
"""Compute inertia for dense input data """Compute inertia for dense input data
Sum of squared distance between each sample and its assigned center. Sum of squared distance between each sample and its assigned center.
If single_label is >= 0, the inertia is computed only for that label. If single_label is >= 0, the inertia is computed only for that label.
""" """
cdef: cdef:
int n_samples = X.shape[0] int n_samples = X.shape[0]
int n_features = X.shape[1] int n_features = X.shape[1]
int i, j int i, j
floating sq_dist = 0.0 floating sq_dist = 0.0
floating inertia = 0.0 floating inertia = 0.0
for i in prange(n_samples, nogil=True, num_threads=n_threads, for i in prange(n_samples, nogil=True, num_threads=n_threads,
schedule='static'): schedule='static'):
j = labels[i] j = labels[i]
if single_label < 0 or single_label == j: if single_label < 0 or single_label == j:
sq_dist = _euclidean_dense_dense(&X[i, 0], &centers[j, 0], sq_dist = _euclidean_dense_dense(&X[i, 0], &centers[j, 0],
n_features, True) n_features, True)
inertia += sq_dist * sample_weight[i] inertia += sq_dist * sample_weight[i]
return inertia return inertia
cpdef floating _inertia_sparse( cpdef floating _inertia_sparse(
X, # IN X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers, # IN const floating[:, ::1] centers, # IN
const int[::1] labels, # IN const int[::1] labels, # IN
int n_threads, int n_threads,
int single_label=-1, int single_label=-1,
): ):
"""Compute inertia for sparse input data """Compute inertia for sparse input data
Sum of squared distance between each sample and its assigned center. Sum of squared distance between each sample and its assigned center.
If single_label is >= 0, the inertia is computed only for that label. If single_label is >= 0, the inertia is computed only for that label.
""" """
cdef: cdef:
floating[::1] X_data = X.data floating[::1] X_data = X.data
int[::1] X_indices = X.indices int[::1] X_indices = X.indices
int[::1] X_indptr = X.indptr int[::1] X_indptr = X.indptr
int n_samples = X.shape[0] int n_samples = X.shape[0]
int i, j int i, j
floating sq_dist = 0.0 floating sq_dist = 0.0
floating inertia = 0.0 floating inertia = 0.0
floating[::1] centers_squared_norms = row_norms(centers, squared=True) floating[::1] centers_squared_norms = row_norms(centers, squared=True)
for i in prange(n_samples, nogil=True, num_threads=n_threads, for i in prange(n_samples, nogil=True, num_threads=n_threads,
schedule='static'): schedule='static'):
j = labels[i] j = labels[i]
if single_label < 0 or single_label == j: if single_label < 0 or single_label == j:
sq_dist = _euclidean_sparse_dense( sq_dist = _euclidean_sparse_dense(
X_data[X_indptr[i]: X_indptr[i + 1]], X_data[X_indptr[i]: X_indptr[i + 1]],
X_indices[X_indptr[i]: X_indptr[i + 1]], X_indices[X_indptr[i]: X_indptr[i + 1]],
centers[j], centers_squared_norms[j], True) centers[j], centers_squared_norms[j], True)
inertia += sq_dist * sample_weight[i] inertia += sq_dist * sample_weight[i]
return inertia return inertia
cpdef void _relocate_empty_clusters_dense( cpdef void _relocate_empty_clusters_dense(
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # INOUT floating[:, ::1] centers_new, # INOUT
floating[::1] weight_in_clusters, # INOUT floating[::1] weight_in_clusters, # INOUT
const int[::1] labels # IN const int[::1] labels # IN
): ):
"""Relocate centers which have no sample assigned to them.""" """Relocate centers which have no sample assigned to them."""
cdef: cdef:
int[::1] empty_clusters = np.where(np.equal(weight_in_clusters, 0))[0].astype(np.int32) int[::1] empty_clusters = np.where(np.equal(weight_in_clusters, 0))[0].astype(np.int32)
int n_empty = empty_clusters.shape[0] int n_empty = empty_clusters.shape[0]
if n_empty == 0: if n_empty == 0:
return return
cdef: cdef:
int n_features = X.shape[1] int n_features = X.shape[1]
floating[::1] distances = ((np.asarray(X) - np.asarray(centers_old)[labels])**2).sum(axis=1) floating[::1] distances = ((np.asarray(X) - np.asarray(centers_old)[labels])**2).sum(axis=1)
int[::1] far_from_centers = np.argpartition(distances, -n_empty)[:-n_empty-1:-1].astype(np.int32) int[::1] far_from_centers = np.argpartition(distances, -n_empty)[:-n_empty-1:-1].astype(np.int32)
int new_cluster_id, old_cluster_id, far_idx, idx, k int new_cluster_id, old_cluster_id, far_idx, idx, k
floating weight floating weight
if np.max(distances) == 0: if np.max(distances) == 0:
# Happens when there are more clusters than non-duplicate samples. Relocating # Happens when there are more clusters than non-duplicate samples. Relocating
# is pointless in this case. # is pointless in this case.
return return
for idx in range(n_empty): for idx in range(n_empty):
new_cluster_id = empty_clusters[idx] new_cluster_id = empty_clusters[idx]
far_idx = far_from_centers[idx] far_idx = far_from_centers[idx]
weight = sample_weight[far_idx] weight = sample_weight[far_idx]
old_cluster_id = labels[far_idx] old_cluster_id = labels[far_idx]
for k in range(n_features): for k in range(n_features):
centers_new[old_cluster_id, k] -= X[far_idx, k] * weight centers_new[old_cluster_id, k] -= X[far_idx, k] * weight
centers_new[new_cluster_id, k] = X[far_idx, k] * weight centers_new[new_cluster_id, k] = X[far_idx, k] * weight
weight_in_clusters[new_cluster_id] = weight weight_in_clusters[new_cluster_id] = weight
weight_in_clusters[old_cluster_id] -= weight weight_in_clusters[old_cluster_id] -= weight
cpdef void _relocate_empty_clusters_sparse( cpdef void _relocate_empty_clusters_sparse(
const floating[::1] X_data, # IN const floating[::1] X_data, # IN
const int[::1] X_indices, # IN const int[::1] X_indices, # IN
const int[::1] X_indptr, # IN const int[::1] X_indptr, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # INOUT floating[:, ::1] centers_new, # INOUT
floating[::1] weight_in_clusters, # INOUT floating[::1] weight_in_clusters, # INOUT
const int[::1] labels # IN const int[::1] labels # IN
): ):
"""Relocate centers which have no sample assigned to them.""" """Relocate centers which have no sample assigned to them."""
cdef: cdef:
int[::1] empty_clusters = np.where(np.equal(weight_in_clusters, 0))[0].astype(np.int32) int[::1] empty_clusters = np.where(np.equal(weight_in_clusters, 0))[0].astype(np.int32)
int n_empty = empty_clusters.shape[0] int n_empty = empty_clusters.shape[0]
if n_empty == 0: if n_empty == 0:
return return
cdef: cdef:
int n_samples = X_indptr.shape[0] - 1 int n_samples = X_indptr.shape[0] - 1
int i, j, k int i, j, k
floating[::1] distances = np.zeros(n_samples, dtype=X_data.base.dtype) floating[::1] distances = np.zeros(n_samples, dtype=X_data.base.dtype)
floating[::1] centers_squared_norms = row_norms(centers_old, squared=True) floating[::1] centers_squared_norms = row_norms(centers_old, squared=True)
for i in range(n_samples): for i in range(n_samples):
j = labels[i] j = labels[i]
distances[i] = _euclidean_sparse_dense( distances[i] = _euclidean_sparse_dense(
X_data[X_indptr[i]: X_indptr[i + 1]], X_data[X_indptr[i]: X_indptr[i + 1]],
X_indices[X_indptr[i]: X_indptr[i + 1]], X_indices[X_indptr[i]: X_indptr[i + 1]],
centers_old[j], centers_squared_norms[j], True) centers_old[j], centers_squared_norms[j], True)
if np.max(distances) == 0: if np.max(distances) == 0:
# Happens when there are more clusters than non-duplicate samples. Relocating # Happens when there are more clusters than non-duplicate samples. Relocating
# is pointless in this case. # is pointless in this case.
return return
cdef: cdef:
int[::1] far_from_centers = np.argpartition(distances, -n_empty)[:-n_empty-1:-1].astype(np.int32) int[::1] far_from_centers = np.argpartition(distances, -n_empty)[:-n_empty-1:-1].astype(np.int32)
int new_cluster_id, old_cluster_id, far_idx, idx int new_cluster_id, old_cluster_id, far_idx, idx
floating weight floating weight
for idx in range(n_empty): for idx in range(n_empty):
new_cluster_id = empty_clusters[idx] new_cluster_id = empty_clusters[idx]
far_idx = far_from_centers[idx] far_idx = far_from_centers[idx]
weight = sample_weight[far_idx] weight = sample_weight[far_idx]
old_cluster_id = labels[far_idx] old_cluster_id = labels[far_idx]
for k in range(X_indptr[far_idx], X_indptr[far_idx + 1]): for k in range(X_indptr[far_idx], X_indptr[far_idx + 1]):
centers_new[old_cluster_id, X_indices[k]] -= X_data[k] * weight centers_new[old_cluster_id, X_indices[k]] -= X_data[k] * weight
centers_new[new_cluster_id, X_indices[k]] = X_data[k] * weight centers_new[new_cluster_id, X_indices[k]] = X_data[k] * weight
weight_in_clusters[new_cluster_id] = weight weight_in_clusters[new_cluster_id] = weight
weight_in_clusters[old_cluster_id] -= weight weight_in_clusters[old_cluster_id] -= weight
cdef void _average_centers( cdef void _average_centers(
floating[:, ::1] centers, # INOUT floating[:, ::1] centers, # INOUT
const floating[::1] weight_in_clusters # IN const floating[::1] weight_in_clusters # IN
): ):
"""Average new centers wrt weights.""" """Average new centers wrt weights."""
cdef: cdef:
int n_clusters = centers.shape[0] int n_clusters = centers.shape[0]
int n_features = centers.shape[1] int n_features = centers.shape[1]
int j, k int j, k
floating alpha floating alpha
int argmax_weight = np.argmax(weight_in_clusters) int argmax_weight = np.argmax(weight_in_clusters)
for j in range(n_clusters): for j in range(n_clusters):
if weight_in_clusters[j] > 0: if weight_in_clusters[j] > 0:
alpha = 1.0 / weight_in_clusters[j] alpha = 1.0 / weight_in_clusters[j]
for k in range(n_features): for k in range(n_features):
centers[j, k] *= alpha centers[j, k] *= alpha
else: else:
# For convenience, we avoid setting empty clusters at the origin but place # For convenience, we avoid setting empty clusters at the origin but place
# them at the location of the biggest cluster. # them at the location of the biggest cluster.
for k in range(n_features): for k in range(n_features):
centers[j, k] = centers[argmax_weight, k] centers[j, k] = centers[argmax_weight, k]
cdef void _center_shift( cdef void _center_shift(
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
const floating[:, ::1] centers_new, # IN const floating[:, ::1] centers_new, # IN
floating[::1] center_shift # OUT floating[::1] center_shift # OUT
): ):
"""Compute shift between old and new centers.""" """Compute shift between old and new centers."""
cdef: cdef:
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
int n_features = centers_old.shape[1] int n_features = centers_old.shape[1]
int j int j
for j in range(n_clusters): for j in range(n_clusters):
center_shift[j] = _euclidean_dense_dense( center_shift[j] = _euclidean_dense_dense(
&centers_new[j, 0], &centers_old[j, 0], n_features, False) &centers_new[j, 0], &centers_old[j, 0], n_features, False)
def _is_same_clustering( def _is_same_clustering(
const int[::1] labels1, const int[::1] labels1,
const int[::1] labels2, const int[::1] labels2,
n_clusters n_clusters
): ):
"""Check if two arrays of labels are the same up to a permutation of the labels""" """Check if two arrays of labels are the same up to a permutation of the labels"""
cdef int[::1] mapping = np.full(fill_value=-1, shape=(n_clusters,), dtype=np.int32) cdef int[::1] mapping = np.full(fill_value=-1, shape=(n_clusters,), dtype=np.int32)
cdef int i cdef int i
for i in range(labels1.shape[0]): for i in range(labels1.shape[0]):
if mapping[labels1[i]] == -1: if mapping[labels1[i]] == -1:
mapping[labels1[i]] = labels2[i] mapping[labels1[i]] = labels2[i]
elif mapping[labels1[i]] != labels2[i]: elif mapping[labels1[i]] != labels2[i]:
return False return False
return True return True
File diff suppressed because it is too large Load Diff
@@ -1,420 +1,420 @@
# Licence: BSD 3 clause # Licence: BSD 3 clause
from cython cimport floating from cython cimport floating
from cython.parallel import prange, parallel from cython.parallel import prange, parallel
from libc.stdlib cimport malloc, calloc, free from libc.stdlib cimport malloc, calloc, free
from libc.string cimport memset from libc.string cimport memset
from libc.float cimport DBL_MAX, FLT_MAX from libc.float cimport DBL_MAX, FLT_MAX
from ..utils._openmp_helpers cimport omp_lock_t from ..utils._openmp_helpers cimport omp_lock_t
from ..utils._openmp_helpers cimport omp_init_lock from ..utils._openmp_helpers cimport omp_init_lock
from ..utils._openmp_helpers cimport omp_destroy_lock from ..utils._openmp_helpers cimport omp_destroy_lock
from ..utils._openmp_helpers cimport omp_set_lock from ..utils._openmp_helpers cimport omp_set_lock
from ..utils._openmp_helpers cimport omp_unset_lock from ..utils._openmp_helpers cimport omp_unset_lock
from ..utils.extmath import row_norms from ..utils.extmath import row_norms
from ..utils._cython_blas cimport _gemm from ..utils._cython_blas cimport _gemm
from ..utils._cython_blas cimport RowMajor, Trans, NoTrans from ..utils._cython_blas cimport RowMajor, Trans, NoTrans
from ._k_means_common import CHUNK_SIZE from ._k_means_common import CHUNK_SIZE
from ._k_means_common cimport _relocate_empty_clusters_dense from ._k_means_common cimport _relocate_empty_clusters_dense
from ._k_means_common cimport _relocate_empty_clusters_sparse from ._k_means_common cimport _relocate_empty_clusters_sparse
from ._k_means_common cimport _average_centers, _center_shift from ._k_means_common cimport _average_centers, _center_shift
def lloyd_iter_chunked_dense( def lloyd_iter_chunked_dense(
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_in_clusters, # OUT floating[::1] weight_in_clusters, # OUT
int[::1] labels, # OUT int[::1] labels, # OUT
floating[::1] center_shift, # OUT floating[::1] center_shift, # OUT
int n_threads, int n_threads,
bint update_centers=True): bint update_centers=True):
"""Single iteration of K-means lloyd algorithm with dense input. """Single iteration of K-means lloyd algorithm with dense input.
Update labels and centers (inplace), for one iteration, distributed Update labels and centers (inplace), for one iteration, distributed
over data chunks. over data chunks.
Parameters Parameters
---------- ----------
X : ndarray of shape (n_samples, n_features), dtype=floating X : ndarray of shape (n_samples, n_features), dtype=floating
The observations to cluster. The observations to cluster.
sample_weight : ndarray of shape (n_samples,), dtype=floating sample_weight : ndarray of shape (n_samples,), dtype=floating
The weights for each observation in X. The weights for each observation in X.
centers_old : ndarray of shape (n_clusters, n_features), dtype=floating centers_old : ndarray of shape (n_clusters, n_features), dtype=floating
Centers before previous iteration, placeholder for the centers after Centers before previous iteration, placeholder for the centers after
previous iteration. previous iteration.
centers_new : ndarray of shape (n_clusters, n_features), dtype=floating centers_new : ndarray of shape (n_clusters, n_features), dtype=floating
Centers after previous iteration, placeholder for the new centers Centers after previous iteration, placeholder for the new centers
computed during this iteration. `centers_new` can be `None` if computed during this iteration. `centers_new` can be `None` if
`update_centers` is False. `update_centers` is False.
weight_in_clusters : ndarray of shape (n_clusters,), dtype=floating weight_in_clusters : ndarray of shape (n_clusters,), dtype=floating
Placeholder for the sums of the weights of every observation assigned Placeholder for the sums of the weights of every observation assigned
to each center. `weight_in_clusters` can be `None` if `update_centers` to each center. `weight_in_clusters` can be `None` if `update_centers`
is False. is False.
labels : ndarray of shape (n_samples,), dtype=int labels : ndarray of shape (n_samples,), dtype=int
labels assignment. labels assignment.
center_shift : ndarray of shape (n_clusters,), dtype=floating center_shift : ndarray of shape (n_clusters,), dtype=floating
Distance between old and new centers. Distance between old and new centers.
n_threads : int n_threads : int
The number of threads to be used by openmp. The number of threads to be used by openmp.
update_centers : bool update_centers : bool
- If True, the labels and the new centers will be computed, i.e. runs - If True, the labels and the new centers will be computed, i.e. runs
the E-step and the M-step of the algorithm. the E-step and the M-step of the algorithm.
- If False, only the labels will be computed, i.e runs the E-step of - If False, only the labels will be computed, i.e runs the E-step of
the algorithm. This is useful especially when calling predict on a the algorithm. This is useful especially when calling predict on a
fitted model. fitted model.
""" """
cdef: cdef:
int n_samples = X.shape[0] int n_samples = X.shape[0]
int n_features = X.shape[1] int n_features = X.shape[1]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
if n_samples == 0: if n_samples == 0:
# An empty array was passed, do nothing and return early (before # An empty array was passed, do nothing and return early (before
# attempting to compute n_chunks). This can typically happen when # attempting to compute n_chunks). This can typically happen when
# calling the prediction function of a bisecting k-means model with a # calling the prediction function of a bisecting k-means model with a
# large fraction of outliers. # large fraction of outliers.
return return
cdef: cdef:
# hard-coded number of samples per chunk. Appeared to be close to # hard-coded number of samples per chunk. Appeared to be close to
# optimal in all situations. # optimal in all situations.
int n_samples_chunk = CHUNK_SIZE if n_samples > CHUNK_SIZE else n_samples int n_samples_chunk = CHUNK_SIZE if n_samples > CHUNK_SIZE else n_samples
int n_chunks = n_samples // n_samples_chunk int n_chunks = n_samples // n_samples_chunk
int n_samples_rem = n_samples % n_samples_chunk int n_samples_rem = n_samples % n_samples_chunk
int chunk_idx int chunk_idx
int start, end int start, end
int j, k int j, k
floating[::1] centers_squared_norms = row_norms(centers_old, squared=True) floating[::1] centers_squared_norms = row_norms(centers_old, squared=True)
floating *centers_new_chunk floating *centers_new_chunk
floating *weight_in_clusters_chunk floating *weight_in_clusters_chunk
floating *pairwise_distances_chunk floating *pairwise_distances_chunk
omp_lock_t lock omp_lock_t lock
# count remainder chunk in total number of chunks # count remainder chunk in total number of chunks
n_chunks += n_samples != n_chunks * n_samples_chunk n_chunks += n_samples != n_chunks * n_samples_chunk
# number of threads should not be bigger than number of chunks # number of threads should not be bigger than number of chunks
n_threads = min(n_threads, n_chunks) n_threads = min(n_threads, n_chunks)
if update_centers: if update_centers:
memset(&centers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&centers_new[0, 0], 0, n_clusters * n_features * sizeof(floating))
memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating))
omp_init_lock(&lock) omp_init_lock(&lock)
with nogil, parallel(num_threads=n_threads): with nogil, parallel(num_threads=n_threads):
# thread local buffers # thread local buffers
centers_new_chunk = <floating*> calloc(n_clusters * n_features, sizeof(floating)) centers_new_chunk = <floating*> calloc(n_clusters * n_features, sizeof(floating))
weight_in_clusters_chunk = <floating*> calloc(n_clusters, sizeof(floating)) weight_in_clusters_chunk = <floating*> calloc(n_clusters, sizeof(floating))
pairwise_distances_chunk = <floating*> malloc(n_samples_chunk * n_clusters * sizeof(floating)) pairwise_distances_chunk = <floating*> malloc(n_samples_chunk * n_clusters * sizeof(floating))
for chunk_idx in prange(n_chunks, schedule='static'): for chunk_idx in prange(n_chunks, schedule='static'):
start = chunk_idx * n_samples_chunk start = chunk_idx * n_samples_chunk
if chunk_idx == n_chunks - 1 and n_samples_rem > 0: if chunk_idx == n_chunks - 1 and n_samples_rem > 0:
end = start + n_samples_rem end = start + n_samples_rem
else: else:
end = start + n_samples_chunk end = start + n_samples_chunk
_update_chunk_dense( _update_chunk_dense(
X[start: end], X[start: end],
sample_weight[start: end], sample_weight[start: end],
centers_old, centers_old,
centers_squared_norms, centers_squared_norms,
labels[start: end], labels[start: end],
centers_new_chunk, centers_new_chunk,
weight_in_clusters_chunk, weight_in_clusters_chunk,
pairwise_distances_chunk, pairwise_distances_chunk,
update_centers) update_centers)
# reduction from local buffers. # reduction from local buffers.
if update_centers: if update_centers:
# The lock is necessary to avoid race conditions when aggregating # The lock is necessary to avoid race conditions when aggregating
# info from different thread-local buffers. # info from different thread-local buffers.
omp_set_lock(&lock) omp_set_lock(&lock)
for j in range(n_clusters): for j in range(n_clusters):
weight_in_clusters[j] += weight_in_clusters_chunk[j] weight_in_clusters[j] += weight_in_clusters_chunk[j]
for k in range(n_features): for k in range(n_features):
centers_new[j, k] += centers_new_chunk[j * n_features + k] centers_new[j, k] += centers_new_chunk[j * n_features + k]
omp_unset_lock(&lock) omp_unset_lock(&lock)
free(centers_new_chunk) free(centers_new_chunk)
free(weight_in_clusters_chunk) free(weight_in_clusters_chunk)
free(pairwise_distances_chunk) free(pairwise_distances_chunk)
if update_centers: if update_centers:
omp_destroy_lock(&lock) omp_destroy_lock(&lock)
_relocate_empty_clusters_dense( _relocate_empty_clusters_dense(
X, sample_weight, centers_old, centers_new, weight_in_clusters, labels X, sample_weight, centers_old, centers_new, weight_in_clusters, labels
) )
_average_centers(centers_new, weight_in_clusters) _average_centers(centers_new, weight_in_clusters)
_center_shift(centers_old, centers_new, center_shift) _center_shift(centers_old, centers_new, center_shift)
cdef void _update_chunk_dense( cdef void _update_chunk_dense(
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
const floating[::1] centers_squared_norms, # IN const floating[::1] centers_squared_norms, # IN
int[::1] labels, # OUT int[::1] labels, # OUT
floating *centers_new, # OUT floating *centers_new, # OUT
floating *weight_in_clusters, # OUT floating *weight_in_clusters, # OUT
floating *pairwise_distances, # OUT floating *pairwise_distances, # OUT
bint update_centers) noexcept nogil: bint update_centers) noexcept nogil:
"""K-means combined EM step for one dense data chunk. """K-means combined EM step for one dense data chunk.
Compute the partial contribution of a single data chunk to the labels and Compute the partial contribution of a single data chunk to the labels and
centers. centers.
""" """
cdef: cdef:
int n_samples = labels.shape[0] int n_samples = labels.shape[0]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
int n_features = centers_old.shape[1] int n_features = centers_old.shape[1]
floating sq_dist, min_sq_dist floating sq_dist, min_sq_dist
int i, j, k, label int i, j, k, label
# Instead of computing the full pairwise squared distances matrix, # Instead of computing the full pairwise squared distances matrix,
# ||X - C||² = ||X||² - 2 X.C^T + ||C||², we only need to store # ||X - C||² = ||X||² - 2 X.C^T + ||C||², we only need to store
# the - 2 X.C^T + ||C||² term since the argmin for a given sample only # the - 2 X.C^T + ||C||² term since the argmin for a given sample only
# depends on the centers. # depends on the centers.
# pairwise_distances = ||C||² # pairwise_distances = ||C||²
for i in range(n_samples): for i in range(n_samples):
for j in range(n_clusters): for j in range(n_clusters):
pairwise_distances[i * n_clusters + j] = centers_squared_norms[j] pairwise_distances[i * n_clusters + j] = centers_squared_norms[j]
# pairwise_distances += -2 * X.dot(C.T) # pairwise_distances += -2 * X.dot(C.T)
_gemm(RowMajor, NoTrans, Trans, n_samples, n_clusters, n_features, _gemm(RowMajor, NoTrans, Trans, n_samples, n_clusters, n_features,
-2.0, &X[0, 0], n_features, &centers_old[0, 0], n_features, -2.0, &X[0, 0], n_features, &centers_old[0, 0], n_features,
1.0, pairwise_distances, n_clusters) 1.0, pairwise_distances, n_clusters)
for i in range(n_samples): for i in range(n_samples):
min_sq_dist = pairwise_distances[i * n_clusters] min_sq_dist = pairwise_distances[i * n_clusters]
label = 0 label = 0
for j in range(1, n_clusters): for j in range(1, n_clusters):
sq_dist = pairwise_distances[i * n_clusters + j] sq_dist = pairwise_distances[i * n_clusters + j]
if sq_dist < min_sq_dist: if sq_dist < min_sq_dist:
min_sq_dist = sq_dist min_sq_dist = sq_dist
label = j label = j
labels[i] = label labels[i] = label
if update_centers: if update_centers:
weight_in_clusters[label] += sample_weight[i] weight_in_clusters[label] += sample_weight[i]
for k in range(n_features): for k in range(n_features):
centers_new[label * n_features + k] += X[i, k] * sample_weight[i] centers_new[label * n_features + k] += X[i, k] * sample_weight[i]
def lloyd_iter_chunked_sparse( def lloyd_iter_chunked_sparse(
X, # IN X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_in_clusters, # OUT floating[::1] weight_in_clusters, # OUT
int[::1] labels, # OUT int[::1] labels, # OUT
floating[::1] center_shift, # OUT floating[::1] center_shift, # OUT
int n_threads, int n_threads,
bint update_centers=True): bint update_centers=True):
"""Single iteration of K-means lloyd algorithm with sparse input. """Single iteration of K-means lloyd algorithm with sparse input.
Update labels and centers (inplace), for one iteration, distributed Update labels and centers (inplace), for one iteration, distributed
over data chunks. over data chunks.
Parameters Parameters
---------- ----------
X : sparse matrix of shape (n_samples, n_features), dtype=floating X : sparse matrix of shape (n_samples, n_features), dtype=floating
The observations to cluster. Must be in CSR format. The observations to cluster. Must be in CSR format.
sample_weight : ndarray of shape (n_samples,), dtype=floating sample_weight : ndarray of shape (n_samples,), dtype=floating
The weights for each observation in X. The weights for each observation in X.
centers_old : ndarray of shape (n_clusters, n_features), dtype=floating centers_old : ndarray of shape (n_clusters, n_features), dtype=floating
Centers before previous iteration, placeholder for the centers after Centers before previous iteration, placeholder for the centers after
previous iteration. previous iteration.
centers_new : ndarray of shape (n_clusters, n_features), dtype=floating centers_new : ndarray of shape (n_clusters, n_features), dtype=floating
Centers after previous iteration, placeholder for the new centers Centers after previous iteration, placeholder for the new centers
computed during this iteration. `centers_new` can be `None` if computed during this iteration. `centers_new` can be `None` if
`update_centers` is False. `update_centers` is False.
weight_in_clusters : ndarray of shape (n_clusters,), dtype=floating weight_in_clusters : ndarray of shape (n_clusters,), dtype=floating
Placeholder for the sums of the weights of every observation assigned Placeholder for the sums of the weights of every observation assigned
to each center. `weight_in_clusters` can be `None` if `update_centers` to each center. `weight_in_clusters` can be `None` if `update_centers`
is False. is False.
labels : ndarray of shape (n_samples,), dtype=int labels : ndarray of shape (n_samples,), dtype=int
labels assignment. labels assignment.
center_shift : ndarray of shape (n_clusters,), dtype=floating center_shift : ndarray of shape (n_clusters,), dtype=floating
Distance between old and new centers. Distance between old and new centers.
n_threads : int n_threads : int
The number of threads to be used by openmp. The number of threads to be used by openmp.
update_centers : bool update_centers : bool
- If True, the labels and the new centers will be computed, i.e. runs - If True, the labels and the new centers will be computed, i.e. runs
the E-step and the M-step of the algorithm. the E-step and the M-step of the algorithm.
- If False, only the labels will be computed, i.e runs the E-step of - If False, only the labels will be computed, i.e runs the E-step of
the algorithm. This is useful especially when calling predict on a the algorithm. This is useful especially when calling predict on a
fitted model. fitted model.
""" """
cdef: cdef:
int n_samples = X.shape[0] int n_samples = X.shape[0]
int n_features = X.shape[1] int n_features = X.shape[1]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
if n_samples == 0: if n_samples == 0:
# An empty array was passed, do nothing and return early (before # An empty array was passed, do nothing and return early (before
# attempting to compute n_chunks). This can typically happen when # attempting to compute n_chunks). This can typically happen when
# calling the prediction function of a bisecting k-means model with a # calling the prediction function of a bisecting k-means model with a
# large fraction of outliers. # large fraction of outliers.
return return
cdef: cdef:
# Choose same as for dense. Does not have the same impact since with # Choose same as for dense. Does not have the same impact since with
# sparse data the pairwise distances matrix is not precomputed. # sparse data the pairwise distances matrix is not precomputed.
# However, splitting in chunks is necessary to get parallelism. # However, splitting in chunks is necessary to get parallelism.
int n_samples_chunk = CHUNK_SIZE if n_samples > CHUNK_SIZE else n_samples int n_samples_chunk = CHUNK_SIZE if n_samples > CHUNK_SIZE else n_samples
int n_chunks = n_samples // n_samples_chunk int n_chunks = n_samples // n_samples_chunk
int n_samples_rem = n_samples % n_samples_chunk int n_samples_rem = n_samples % n_samples_chunk
int chunk_idx int chunk_idx
int start = 0, end = 0 int start = 0, end = 0
int j, k int j, k
floating[::1] X_data = X.data floating[::1] X_data = X.data
int[::1] X_indices = X.indices int[::1] X_indices = X.indices
int[::1] X_indptr = X.indptr int[::1] X_indptr = X.indptr
floating[::1] centers_squared_norms = row_norms(centers_old, squared=True) floating[::1] centers_squared_norms = row_norms(centers_old, squared=True)
floating *centers_new_chunk floating *centers_new_chunk
floating *weight_in_clusters_chunk floating *weight_in_clusters_chunk
omp_lock_t lock omp_lock_t lock
# count remainder chunk in total number of chunks # count remainder chunk in total number of chunks
n_chunks += n_samples != n_chunks * n_samples_chunk n_chunks += n_samples != n_chunks * n_samples_chunk
# number of threads should not be bigger than number of chunks # number of threads should not be bigger than number of chunks
n_threads = min(n_threads, n_chunks) n_threads = min(n_threads, n_chunks)
if update_centers: if update_centers:
memset(&centers_new[0, 0], 0, n_clusters * n_features * sizeof(floating)) memset(&centers_new[0, 0], 0, n_clusters * n_features * sizeof(floating))
memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating)) memset(&weight_in_clusters[0], 0, n_clusters * sizeof(floating))
omp_init_lock(&lock) omp_init_lock(&lock)
with nogil, parallel(num_threads=n_threads): with nogil, parallel(num_threads=n_threads):
# thread local buffers # thread local buffers
centers_new_chunk = <floating*> calloc(n_clusters * n_features, sizeof(floating)) centers_new_chunk = <floating*> calloc(n_clusters * n_features, sizeof(floating))
weight_in_clusters_chunk = <floating*> calloc(n_clusters, sizeof(floating)) weight_in_clusters_chunk = <floating*> calloc(n_clusters, sizeof(floating))
for chunk_idx in prange(n_chunks, schedule='static'): for chunk_idx in prange(n_chunks, schedule='static'):
start = chunk_idx * n_samples_chunk start = chunk_idx * n_samples_chunk
if chunk_idx == n_chunks - 1 and n_samples_rem > 0: if chunk_idx == n_chunks - 1 and n_samples_rem > 0:
end = start + n_samples_rem end = start + n_samples_rem
else: else:
end = start + n_samples_chunk end = start + n_samples_chunk
_update_chunk_sparse( _update_chunk_sparse(
X_data[X_indptr[start]: X_indptr[end]], X_data[X_indptr[start]: X_indptr[end]],
X_indices[X_indptr[start]: X_indptr[end]], X_indices[X_indptr[start]: X_indptr[end]],
X_indptr[start: end+1], X_indptr[start: end+1],
sample_weight[start: end], sample_weight[start: end],
centers_old, centers_old,
centers_squared_norms, centers_squared_norms,
labels[start: end], labels[start: end],
centers_new_chunk, centers_new_chunk,
weight_in_clusters_chunk, weight_in_clusters_chunk,
update_centers) update_centers)
# reduction from local buffers. # reduction from local buffers.
if update_centers: if update_centers:
# The lock is necessary to avoid race conditions when aggregating # The lock is necessary to avoid race conditions when aggregating
# info from different thread-local buffers. # info from different thread-local buffers.
omp_set_lock(&lock) omp_set_lock(&lock)
for j in range(n_clusters): for j in range(n_clusters):
weight_in_clusters[j] += weight_in_clusters_chunk[j] weight_in_clusters[j] += weight_in_clusters_chunk[j]
for k in range(n_features): for k in range(n_features):
centers_new[j, k] += centers_new_chunk[j * n_features + k] centers_new[j, k] += centers_new_chunk[j * n_features + k]
omp_unset_lock(&lock) omp_unset_lock(&lock)
free(centers_new_chunk) free(centers_new_chunk)
free(weight_in_clusters_chunk) free(weight_in_clusters_chunk)
if update_centers: if update_centers:
omp_destroy_lock(&lock) omp_destroy_lock(&lock)
_relocate_empty_clusters_sparse( _relocate_empty_clusters_sparse(
X_data, X_indices, X_indptr, sample_weight, X_data, X_indices, X_indptr, sample_weight,
centers_old, centers_new, weight_in_clusters, labels) centers_old, centers_new, weight_in_clusters, labels)
_average_centers(centers_new, weight_in_clusters) _average_centers(centers_new, weight_in_clusters)
_center_shift(centers_old, centers_new, center_shift) _center_shift(centers_old, centers_new, center_shift)
cdef void _update_chunk_sparse( cdef void _update_chunk_sparse(
const floating[::1] X_data, # IN const floating[::1] X_data, # IN
const int[::1] X_indices, # IN const int[::1] X_indices, # IN
const int[::1] X_indptr, # IN const int[::1] X_indptr, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
const floating[::1] centers_squared_norms, # IN const floating[::1] centers_squared_norms, # IN
int[::1] labels, # OUT int[::1] labels, # OUT
floating *centers_new, # OUT floating *centers_new, # OUT
floating *weight_in_clusters, # OUT floating *weight_in_clusters, # OUT
bint update_centers) noexcept nogil: bint update_centers) noexcept nogil:
"""K-means combined EM step for one sparse data chunk. """K-means combined EM step for one sparse data chunk.
Compute the partial contribution of a single data chunk to the labels and Compute the partial contribution of a single data chunk to the labels and
centers. centers.
""" """
cdef: cdef:
int n_samples = labels.shape[0] int n_samples = labels.shape[0]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
int n_features = centers_old.shape[1] int n_features = centers_old.shape[1]
floating sq_dist, min_sq_dist floating sq_dist, min_sq_dist
int i, j, k, label int i, j, k, label
floating max_floating = FLT_MAX if floating is float else DBL_MAX floating max_floating = FLT_MAX if floating is float else DBL_MAX
int s = X_indptr[0] int s = X_indptr[0]
# XXX Precompute the pairwise distances matrix is not worth for sparse # XXX Precompute the pairwise distances matrix is not worth for sparse
# currently. Should be tested when BLAS (sparse x dense) matrix # currently. Should be tested when BLAS (sparse x dense) matrix
# multiplication is available. # multiplication is available.
for i in range(n_samples): for i in range(n_samples):
min_sq_dist = max_floating min_sq_dist = max_floating
label = 0 label = 0
for j in range(n_clusters): for j in range(n_clusters):
sq_dist = 0.0 sq_dist = 0.0
for k in range(X_indptr[i] - s, X_indptr[i + 1] - s): for k in range(X_indptr[i] - s, X_indptr[i + 1] - s):
sq_dist += centers_old[j, X_indices[k]] * X_data[k] sq_dist += centers_old[j, X_indices[k]] * X_data[k]
# Instead of computing the full squared distance with each cluster, # Instead of computing the full squared distance with each cluster,
# ||X - C||² = ||X||² - 2 X.C^T + ||C||², we only need to compute # ||X - C||² = ||X||² - 2 X.C^T + ||C||², we only need to compute
# the - 2 X.C^T + ||C||² term since the argmin for a given sample # the - 2 X.C^T + ||C||² term since the argmin for a given sample
# only depends on the centers C. # only depends on the centers C.
sq_dist = centers_squared_norms[j] -2 * sq_dist sq_dist = centers_squared_norms[j] -2 * sq_dist
if sq_dist < min_sq_dist: if sq_dist < min_sq_dist:
min_sq_dist = sq_dist min_sq_dist = sq_dist
label = j label = j
labels[i] = label labels[i] = label
if update_centers: if update_centers:
weight_in_clusters[label] += sample_weight[i] weight_in_clusters[label] += sample_weight[i]
for k in range(X_indptr[i] - s, X_indptr[i + 1] - s): for k in range(X_indptr[i] - s, X_indptr[i + 1] - s):
centers_new[label * n_features + X_indices[k]] += X_data[k] * sample_weight[i] centers_new[label * n_features + X_indices[k]] += X_data[k] * sample_weight[i]
@@ -1,218 +1,218 @@
from cython cimport floating from cython cimport floating
from cython.parallel cimport parallel, prange from cython.parallel cimport parallel, prange
from libc.stdlib cimport malloc, free from libc.stdlib cimport malloc, free
def _minibatch_update_dense( def _minibatch_update_dense(
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_sums, # INOUT floating[::1] weight_sums, # INOUT
const int[::1] labels, # IN const int[::1] labels, # IN
int n_threads): int n_threads):
"""Update of the centers for dense MiniBatchKMeans. """Update of the centers for dense MiniBatchKMeans.
Parameters Parameters
---------- ----------
X : ndarray of shape (n_samples, n_features), dtype=floating X : ndarray of shape (n_samples, n_features), dtype=floating
The observations to cluster. The observations to cluster.
sample_weight : ndarray of shape (n_samples,), dtype=floating sample_weight : ndarray of shape (n_samples,), dtype=floating
The weights for each observation in X. The weights for each observation in X.
centers_old : ndarray of shape (n_clusters, n_features), dtype=floating centers_old : ndarray of shape (n_clusters, n_features), dtype=floating
Centers before previous iteration, placeholder for the centers after Centers before previous iteration, placeholder for the centers after
previous iteration. previous iteration.
centers_new : ndarray of shape (n_clusters, n_features), dtype=floating centers_new : ndarray of shape (n_clusters, n_features), dtype=floating
Centers after previous iteration, placeholder for the new centers Centers after previous iteration, placeholder for the new centers
computed during this iteration. computed during this iteration.
weight_sums : ndarray of shape (n_clusters,), dtype=floating weight_sums : ndarray of shape (n_clusters,), dtype=floating
Current sums of the accumulated weights for each center. Current sums of the accumulated weights for each center.
labels : ndarray of shape (n_samples,), dtype=int labels : ndarray of shape (n_samples,), dtype=int
labels assignment. labels assignment.
n_threads : int n_threads : int
The number of threads to be used by openmp. The number of threads to be used by openmp.
""" """
cdef: cdef:
int n_samples = X.shape[0] int n_samples = X.shape[0]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
int cluster_idx int cluster_idx
int *indices int *indices
with nogil, parallel(num_threads=n_threads): with nogil, parallel(num_threads=n_threads):
indices = <int*> malloc(n_samples * sizeof(int)) indices = <int*> malloc(n_samples * sizeof(int))
for cluster_idx in prange(n_clusters, schedule="static"): for cluster_idx in prange(n_clusters, schedule="static"):
update_center_dense(cluster_idx, X, sample_weight, update_center_dense(cluster_idx, X, sample_weight,
centers_old, centers_new, weight_sums, labels, centers_old, centers_new, weight_sums, labels,
indices) indices)
free(indices) free(indices)
cdef void update_center_dense( cdef void update_center_dense(
int cluster_idx, int cluster_idx,
const floating[:, ::1] X, # IN const floating[:, ::1] X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_sums, # INOUT floating[::1] weight_sums, # INOUT
const int[::1] labels, # IN const int[::1] labels, # IN
int *indices) noexcept nogil: # TMP int *indices) noexcept nogil: # TMP
"""Update of a single center for dense MinibatchKMeans""" """Update of a single center for dense MinibatchKMeans"""
cdef: cdef:
int n_samples = sample_weight.shape[0] int n_samples = sample_weight.shape[0]
int n_features = centers_old.shape[1] int n_features = centers_old.shape[1]
floating alpha floating alpha
int n_indices int n_indices
int k, sample_idx, feature_idx int k, sample_idx, feature_idx
floating wsum = 0 floating wsum = 0
# indices = np.where(labels == cluster_idx)[0] # indices = np.where(labels == cluster_idx)[0]
k = 0 k = 0
for sample_idx in range(n_samples): for sample_idx in range(n_samples):
if labels[sample_idx] == cluster_idx: if labels[sample_idx] == cluster_idx:
indices[k] = sample_idx indices[k] = sample_idx
wsum += sample_weight[sample_idx] wsum += sample_weight[sample_idx]
k += 1 k += 1
n_indices = k n_indices = k
if wsum > 0: if wsum > 0:
# Undo the previous count-based scaling for this cluster center # Undo the previous count-based scaling for this cluster center
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] * weight_sums[cluster_idx] centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] * weight_sums[cluster_idx]
# Update cluster with new point members # Update cluster with new point members
for k in range(n_indices): for k in range(n_indices):
sample_idx = indices[k] sample_idx = indices[k]
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] += X[sample_idx, feature_idx] * sample_weight[sample_idx] centers_new[cluster_idx, feature_idx] += X[sample_idx, feature_idx] * sample_weight[sample_idx]
# Update the count statistics for this center # Update the count statistics for this center
weight_sums[cluster_idx] += wsum weight_sums[cluster_idx] += wsum
# Rescale to compute mean of all points (old and new) # Rescale to compute mean of all points (old and new)
alpha = 1 / weight_sums[cluster_idx] alpha = 1 / weight_sums[cluster_idx]
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] *= alpha centers_new[cluster_idx, feature_idx] *= alpha
else: else:
# No sample was assigned to this cluster in this batch of data # No sample was assigned to this cluster in this batch of data
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx]
def _minibatch_update_sparse( def _minibatch_update_sparse(
X, # IN X, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_sums, # INOUT floating[::1] weight_sums, # INOUT
const int[::1] labels, # IN const int[::1] labels, # IN
int n_threads): int n_threads):
"""Update of the centers for sparse MiniBatchKMeans. """Update of the centers for sparse MiniBatchKMeans.
Parameters Parameters
---------- ----------
X : sparse matrix of shape (n_samples, n_features), dtype=floating X : sparse matrix of shape (n_samples, n_features), dtype=floating
The observations to cluster. Must be in CSR format. The observations to cluster. Must be in CSR format.
sample_weight : ndarray of shape (n_samples,), dtype=floating sample_weight : ndarray of shape (n_samples,), dtype=floating
The weights for each observation in X. The weights for each observation in X.
centers_old : ndarray of shape (n_clusters, n_features), dtype=floating centers_old : ndarray of shape (n_clusters, n_features), dtype=floating
Centers before previous iteration, placeholder for the centers after Centers before previous iteration, placeholder for the centers after
previous iteration. previous iteration.
centers_new : ndarray of shape (n_clusters, n_features), dtype=floating centers_new : ndarray of shape (n_clusters, n_features), dtype=floating
Centers after previous iteration, placeholder for the new centers Centers after previous iteration, placeholder for the new centers
computed during this iteration. computed during this iteration.
weight_sums : ndarray of shape (n_clusters,), dtype=floating weight_sums : ndarray of shape (n_clusters,), dtype=floating
Current sums of the accumulated weights for each center. Current sums of the accumulated weights for each center.
labels : ndarray of shape (n_samples,), dtype=int labels : ndarray of shape (n_samples,), dtype=int
labels assignment. labels assignment.
n_threads : int n_threads : int
The number of threads to be used by openmp. The number of threads to be used by openmp.
""" """
cdef: cdef:
floating[::1] X_data = X.data floating[::1] X_data = X.data
int[::1] X_indices = X.indices int[::1] X_indices = X.indices
int[::1] X_indptr = X.indptr int[::1] X_indptr = X.indptr
int n_samples = X.shape[0] int n_samples = X.shape[0]
int n_clusters = centers_old.shape[0] int n_clusters = centers_old.shape[0]
int cluster_idx int cluster_idx
int *indices int *indices
with nogil, parallel(num_threads=n_threads): with nogil, parallel(num_threads=n_threads):
indices = <int*> malloc(n_samples * sizeof(int)) indices = <int*> malloc(n_samples * sizeof(int))
for cluster_idx in prange(n_clusters, schedule="static"): for cluster_idx in prange(n_clusters, schedule="static"):
update_center_sparse(cluster_idx, X_data, X_indices, X_indptr, update_center_sparse(cluster_idx, X_data, X_indices, X_indptr,
sample_weight, centers_old, centers_new, sample_weight, centers_old, centers_new,
weight_sums, labels, indices) weight_sums, labels, indices)
free(indices) free(indices)
cdef void update_center_sparse( cdef void update_center_sparse(
int cluster_idx, int cluster_idx,
const floating[::1] X_data, # IN const floating[::1] X_data, # IN
const int[::1] X_indices, # IN const int[::1] X_indices, # IN
const int[::1] X_indptr, # IN const int[::1] X_indptr, # IN
const floating[::1] sample_weight, # IN const floating[::1] sample_weight, # IN
const floating[:, ::1] centers_old, # IN const floating[:, ::1] centers_old, # IN
floating[:, ::1] centers_new, # OUT floating[:, ::1] centers_new, # OUT
floating[::1] weight_sums, # INOUT floating[::1] weight_sums, # INOUT
const int[::1] labels, # IN const int[::1] labels, # IN
int *indices) noexcept nogil: # TMP int *indices) noexcept nogil: # TMP
"""Update of a single center for sparse MinibatchKMeans""" """Update of a single center for sparse MinibatchKMeans"""
cdef: cdef:
int n_samples = sample_weight.shape[0] int n_samples = sample_weight.shape[0]
int n_features = centers_old.shape[1] int n_features = centers_old.shape[1]
floating alpha floating alpha
int n_indices int n_indices
int k, sample_idx, feature_idx int k, sample_idx, feature_idx
floating wsum = 0 floating wsum = 0
# indices = np.where(labels == cluster_idx)[0] # indices = np.where(labels == cluster_idx)[0]
k = 0 k = 0
for sample_idx in range(n_samples): for sample_idx in range(n_samples):
if labels[sample_idx] == cluster_idx: if labels[sample_idx] == cluster_idx:
indices[k] = sample_idx indices[k] = sample_idx
wsum += sample_weight[sample_idx] wsum += sample_weight[sample_idx]
k += 1 k += 1
n_indices = k n_indices = k
if wsum > 0: if wsum > 0:
# Undo the previous count-based scaling for this cluster center: # Undo the previous count-based scaling for this cluster center:
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] * weight_sums[cluster_idx] centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] * weight_sums[cluster_idx]
# Update cluster with new point members # Update cluster with new point members
for k in range(n_indices): for k in range(n_indices):
sample_idx = indices[k] sample_idx = indices[k]
for feature_idx in range(X_indptr[sample_idx], X_indptr[sample_idx + 1]): for feature_idx in range(X_indptr[sample_idx], X_indptr[sample_idx + 1]):
centers_new[cluster_idx, X_indices[feature_idx]] += X_data[feature_idx] * sample_weight[sample_idx] centers_new[cluster_idx, X_indices[feature_idx]] += X_data[feature_idx] * sample_weight[sample_idx]
# Update the count statistics for this center # Update the count statistics for this center
weight_sums[cluster_idx] += wsum weight_sums[cluster_idx] += wsum
# Rescale to compute mean of all points (old and new) # Rescale to compute mean of all points (old and new)
alpha = 1 / weight_sums[cluster_idx] alpha = 1 / weight_sums[cluster_idx]
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] *= alpha centers_new[cluster_idx, feature_idx] *= alpha
else: else:
# No sample was assigned to this cluster in this batch of data # No sample was assigned to this cluster in this batch of data
for feature_idx in range(n_features): for feature_idx in range(n_features):
centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx] centers_new[cluster_idx, feature_idx] = centers_old[cluster_idx, feature_idx]
@@ -1,26 +1,26 @@
cluster_extension_metadata = { cluster_extension_metadata = {
'_dbscan_inner': '_dbscan_inner':
{'sources': [cython_gen_cpp.process('_dbscan_inner.pyx')]}, {'sources': [cython_gen_cpp.process('_dbscan_inner.pyx')]},
'_hierarchical_fast': '_hierarchical_fast':
{'sources': [cython_gen_cpp.process('_hierarchical_fast.pyx'), metrics_cython_tree]}, {'sources': [cython_gen_cpp.process('_hierarchical_fast.pyx'), metrics_cython_tree]},
'_k_means_common': '_k_means_common':
{'sources': [cython_gen.process('_k_means_common.pyx')], 'dependencies': [openmp_dep]}, {'sources': [cython_gen.process('_k_means_common.pyx')], 'dependencies': [openmp_dep]},
'_k_means_lloyd': '_k_means_lloyd':
{'sources': [cython_gen.process('_k_means_lloyd.pyx')], 'dependencies': [openmp_dep]}, {'sources': [cython_gen.process('_k_means_lloyd.pyx')], 'dependencies': [openmp_dep]},
'_k_means_elkan': '_k_means_elkan':
{'sources': [cython_gen.process('_k_means_elkan.pyx')], 'dependencies': [openmp_dep]}, {'sources': [cython_gen.process('_k_means_elkan.pyx')], 'dependencies': [openmp_dep]},
'_k_means_minibatch': '_k_means_minibatch':
{'sources': [cython_gen.process('_k_means_minibatch.pyx')], 'dependencies': [openmp_dep]}, {'sources': [cython_gen.process('_k_means_minibatch.pyx')], 'dependencies': [openmp_dep]},
} }
foreach ext_name, ext_dict : cluster_extension_metadata foreach ext_name, ext_dict : cluster_extension_metadata
py.extension_module( py.extension_module(
ext_name, ext_name,
[ext_dict.get('sources'), utils_cython_tree], [ext_dict.get('sources'), utils_cython_tree],
dependencies: [np_dep] + ext_dict.get('dependencies', []), dependencies: [np_dep] + ext_dict.get('dependencies', []),
subdir: 'sklearn/cluster', subdir: 'sklearn/cluster',
install: true install: true
) )
endforeach endforeach
subdir('_hdbscan') subdir('_hdbscan')
@@ -1,252 +1,252 @@
# Optimized inner loop of load_svmlight_file. # Optimized inner loop of load_svmlight_file.
# #
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
import array import array
from cpython cimport array from cpython cimport array
cimport cython cimport cython
from libc.string cimport strchr from libc.string cimport strchr
import numpy as np import numpy as np
cdef bytes COMMA = u','.encode('ascii') cdef bytes COMMA = u','.encode('ascii')
cdef bytes COLON = u':'.encode('ascii') cdef bytes COLON = u':'.encode('ascii')
def _load_svmlight_file(f, dtype, bint multilabel, bint zero_based, def _load_svmlight_file(f, dtype, bint multilabel, bint zero_based,
bint query_id, long long offset, long long length): bint query_id, long long offset, long long length):
cdef array.array data, indices, indptr cdef array.array data, indices, indptr
cdef bytes line cdef bytes line
cdef char *hash_ptr cdef char *hash_ptr
cdef char *line_cstr cdef char *line_cstr
cdef int idx, prev_idx cdef int idx, prev_idx
cdef Py_ssize_t i cdef Py_ssize_t i
cdef bytes qid_prefix = b'qid' cdef bytes qid_prefix = b'qid'
cdef Py_ssize_t n_features cdef Py_ssize_t n_features
cdef long long offset_max = offset + length if length > 0 else -1 cdef long long offset_max = offset + length if length > 0 else -1
# Special-case float32 but use float64 for everything else; # Special-case float32 but use float64 for everything else;
# the Python code will do further conversions. # the Python code will do further conversions.
if dtype == np.float32: if dtype == np.float32:
data = array.array("f") data = array.array("f")
else: else:
dtype = np.float64 dtype = np.float64
data = array.array("d") data = array.array("d")
indices = array.array("q") indices = array.array("q")
indptr = array.array("q", [0]) indptr = array.array("q", [0])
query = np.arange(0, dtype=np.int64) query = np.arange(0, dtype=np.int64)
if multilabel: if multilabel:
labels = [] labels = []
else: else:
labels = array.array("d") labels = array.array("d")
if offset > 0: if offset > 0:
f.seek(offset) f.seek(offset)
# drop the current line that might be truncated and is to be # drop the current line that might be truncated and is to be
# fetched by another call # fetched by another call
f.readline() f.readline()
for line in f: for line in f:
# skip comments # skip comments
line_cstr = line line_cstr = line
hash_ptr = strchr(line_cstr, 35) # ASCII value of '#' is 35 hash_ptr = strchr(line_cstr, 35) # ASCII value of '#' is 35
if hash_ptr != NULL: if hash_ptr != NULL:
line = line[:hash_ptr - line_cstr] line = line[:hash_ptr - line_cstr]
line_parts = line.split() line_parts = line.split()
if len(line_parts) == 0: if len(line_parts) == 0:
continue continue
target, features = line_parts[0], line_parts[1:] target, features = line_parts[0], line_parts[1:]
if multilabel: if multilabel:
if COLON in target: if COLON in target:
target, features = [], line_parts[0:] target, features = [], line_parts[0:]
else: else:
target = [float(y) for y in target.split(COMMA)] target = [float(y) for y in target.split(COMMA)]
target.sort() target.sort()
labels.append(tuple(target)) labels.append(tuple(target))
else: else:
array.resize_smart(labels, len(labels) + 1) array.resize_smart(labels, len(labels) + 1)
labels[len(labels) - 1] = float(target) labels[len(labels) - 1] = float(target)
prev_idx = -1 prev_idx = -1
n_features = len(features) n_features = len(features)
if n_features and features[0].startswith(qid_prefix): if n_features and features[0].startswith(qid_prefix):
_, value = features[0].split(COLON, 1) _, value = features[0].split(COLON, 1)
if query_id: if query_id:
query.resize(len(query) + 1) query.resize(len(query) + 1)
query[len(query) - 1] = np.int64(value) query[len(query) - 1] = np.int64(value)
features.pop(0) features.pop(0)
n_features -= 1 n_features -= 1
for i in range(0, n_features): for i in range(0, n_features):
idx_s, value = features[i].split(COLON, 1) idx_s, value = features[i].split(COLON, 1)
idx = int(idx_s) idx = int(idx_s)
if idx < 0 or not zero_based and idx == 0: if idx < 0 or not zero_based and idx == 0:
raise ValueError( raise ValueError(
"Invalid index %d in SVMlight/LibSVM data file." % idx) "Invalid index %d in SVMlight/LibSVM data file." % idx)
if idx <= prev_idx: if idx <= prev_idx:
raise ValueError("Feature indices in SVMlight/LibSVM data " raise ValueError("Feature indices in SVMlight/LibSVM data "
"file should be sorted and unique.") "file should be sorted and unique.")
array.resize_smart(indices, len(indices) + 1) array.resize_smart(indices, len(indices) + 1)
indices[len(indices) - 1] = idx indices[len(indices) - 1] = idx
array.resize_smart(data, len(data) + 1) array.resize_smart(data, len(data) + 1)
data[len(data) - 1] = float(value) data[len(data) - 1] = float(value)
prev_idx = idx prev_idx = idx
# increment index pointer array size # increment index pointer array size
array.resize_smart(indptr, len(indptr) + 1) array.resize_smart(indptr, len(indptr) + 1)
indptr[len(indptr) - 1] = len(data) indptr[len(indptr) - 1] = len(data)
if offset_max != -1 and f.tell() > offset_max: if offset_max != -1 and f.tell() > offset_max:
# Stop here and let another call deal with the following. # Stop here and let another call deal with the following.
break break
return (dtype, data, indices, indptr, labels, query) return (dtype, data, indices, indptr, labels, query)
# Two fused types are defined to be able to # Two fused types are defined to be able to
# use all possible combinations of parameters. # use all possible combinations of parameters.
ctypedef fused int_or_float: ctypedef fused int_or_float:
cython.integral cython.integral
cython.floating cython.floating
signed long long signed long long
ctypedef fused double_or_longlong: ctypedef fused double_or_longlong:
double double
signed long long signed long long
ctypedef fused int_or_longlong: ctypedef fused int_or_longlong:
cython.integral cython.integral
signed long long signed long long
def get_dense_row_string( def get_dense_row_string(
const int_or_float[:, :] X, const int_or_float[:, :] X,
Py_ssize_t[:] x_inds, Py_ssize_t[:] x_inds,
double_or_longlong[:] x_vals, double_or_longlong[:] x_vals,
Py_ssize_t row, Py_ssize_t row,
str value_pattern, str value_pattern,
bint one_based, bint one_based,
): ):
cdef: cdef:
Py_ssize_t row_length = X.shape[1] Py_ssize_t row_length = X.shape[1]
Py_ssize_t x_nz_used = 0 Py_ssize_t x_nz_used = 0
Py_ssize_t k Py_ssize_t k
int_or_float val int_or_float val
for k in range(row_length): for k in range(row_length):
val = X[row, k] val = X[row, k]
if val == 0: if val == 0:
continue continue
x_inds[x_nz_used] = k x_inds[x_nz_used] = k
x_vals[x_nz_used] = <double_or_longlong> val x_vals[x_nz_used] = <double_or_longlong> val
x_nz_used += 1 x_nz_used += 1
reprs = [ reprs = [
value_pattern % (x_inds[i] + one_based, x_vals[i]) value_pattern % (x_inds[i] + one_based, x_vals[i])
for i in range(x_nz_used) for i in range(x_nz_used)
] ]
return " ".join(reprs) return " ".join(reprs)
def get_sparse_row_string( def get_sparse_row_string(
int_or_float[:] X_data, int_or_float[:] X_data,
int[:] X_indptr, int[:] X_indptr,
int[:] X_indices, int[:] X_indices,
Py_ssize_t row, Py_ssize_t row,
str value_pattern, str value_pattern,
bint one_based, bint one_based,
): ):
cdef: cdef:
Py_ssize_t row_start = X_indptr[row] Py_ssize_t row_start = X_indptr[row]
Py_ssize_t row_end = X_indptr[row+1] Py_ssize_t row_end = X_indptr[row+1]
reprs = [ reprs = [
value_pattern % (X_indices[i] + one_based, X_data[i]) value_pattern % (X_indices[i] + one_based, X_data[i])
for i in range(row_start, row_end) for i in range(row_start, row_end)
] ]
return " ".join(reprs) return " ".join(reprs)
def _dump_svmlight_file( def _dump_svmlight_file(
X, X,
y, y,
f, f,
bint multilabel, bint multilabel,
bint one_based, bint one_based,
int_or_longlong[:] query_id, int_or_longlong[:] query_id,
bint X_is_sp, bint X_is_sp,
bint y_is_sp, bint y_is_sp,
): ):
cdef bint X_is_integral cdef bint X_is_integral
cdef bint query_id_is_not_empty = query_id.size > 0 cdef bint query_id_is_not_empty = query_id.size > 0
X_is_integral = X.dtype.kind == "i" X_is_integral = X.dtype.kind == "i"
if X_is_integral: if X_is_integral:
value_pattern = "%d:%d" value_pattern = "%d:%d"
else: else:
value_pattern = "%d:%.16g" value_pattern = "%d:%.16g"
if y.dtype.kind == "i": if y.dtype.kind == "i":
label_pattern = "%d" label_pattern = "%d"
else: else:
label_pattern = "%.16g" label_pattern = "%.16g"
line_pattern = "%s" line_pattern = "%s"
if query_id_is_not_empty: if query_id_is_not_empty:
line_pattern += " qid:%d" line_pattern += " qid:%d"
line_pattern += " %s\n" line_pattern += " %s\n"
cdef: cdef:
Py_ssize_t num_labels = y.shape[1] Py_ssize_t num_labels = y.shape[1]
Py_ssize_t x_len = X.shape[0] Py_ssize_t x_len = X.shape[0]
Py_ssize_t row_length = X.shape[1] Py_ssize_t row_length = X.shape[1]
Py_ssize_t i Py_ssize_t i
Py_ssize_t j Py_ssize_t j
Py_ssize_t col_start Py_ssize_t col_start
Py_ssize_t col_end Py_ssize_t col_end
Py_ssize_t[:] x_inds = np.empty(row_length, dtype=np.intp) Py_ssize_t[:] x_inds = np.empty(row_length, dtype=np.intp)
signed long long[:] x_vals_int signed long long[:] x_vals_int
double[:] x_vals_float double[:] x_vals_float
if not X_is_sp: if not X_is_sp:
if X_is_integral: if X_is_integral:
x_vals_int = np.zeros(row_length, dtype=np.longlong) x_vals_int = np.zeros(row_length, dtype=np.longlong)
else: else:
x_vals_float = np.zeros(row_length, dtype=np.float64) x_vals_float = np.zeros(row_length, dtype=np.float64)
for i in range(x_len): for i in range(x_len):
if not X_is_sp: if not X_is_sp:
if X_is_integral: if X_is_integral:
s = get_dense_row_string(X, x_inds, x_vals_int, i, value_pattern, one_based) s = get_dense_row_string(X, x_inds, x_vals_int, i, value_pattern, one_based)
else: else:
s = get_dense_row_string(X, x_inds, x_vals_float, i, value_pattern, one_based) s = get_dense_row_string(X, x_inds, x_vals_float, i, value_pattern, one_based)
else: else:
s = get_sparse_row_string(X.data, X.indptr, X.indices, i, value_pattern, one_based) s = get_sparse_row_string(X.data, X.indptr, X.indices, i, value_pattern, one_based)
if multilabel: if multilabel:
if y_is_sp: if y_is_sp:
col_start = y.indptr[i] col_start = y.indptr[i]
col_end = y.indptr[i+1] col_end = y.indptr[i+1]
labels_str = ','.join(tuple(label_pattern % y.indices[j] for j in range(col_start, col_end) if y.data[j] != 0)) labels_str = ','.join(tuple(label_pattern % y.indices[j] for j in range(col_start, col_end) if y.data[j] != 0))
else: else:
labels_str = ','.join(label_pattern % j for j in range(num_labels) if y[i, j] != 0) labels_str = ','.join(label_pattern % j for j in range(num_labels) if y[i, j] != 0)
else: else:
if y_is_sp: if y_is_sp:
labels_str = label_pattern % y.data[i] labels_str = label_pattern % y.data[i]
else: else:
labels_str = label_pattern % y[i, 0] labels_str = label_pattern % y[i, 0]
if query_id_is_not_empty: if query_id_is_not_empty:
feat = (labels_str, query_id[i], s) feat = (labels_str, query_id[i], s)
else: else:
feat = (labels_str, s) feat = (labels_str, s)
f.write((line_pattern % feat).encode("utf-8")) f.write((line_pattern % feat).encode("utf-8"))
File diff suppressed because it is too large Load Diff
@@ -1,151 +1,151 @@
150,4,setosa,versicolor,virginica 150,4,setosa,versicolor,virginica
5.1,3.5,1.4,0.2,0 5.1,3.5,1.4,0.2,0
4.9,3.0,1.4,0.2,0 4.9,3.0,1.4,0.2,0
4.7,3.2,1.3,0.2,0 4.7,3.2,1.3,0.2,0
4.6,3.1,1.5,0.2,0 4.6,3.1,1.5,0.2,0
5.0,3.6,1.4,0.2,0 5.0,3.6,1.4,0.2,0
5.4,3.9,1.7,0.4,0 5.4,3.9,1.7,0.4,0
4.6,3.4,1.4,0.3,0 4.6,3.4,1.4,0.3,0
5.0,3.4,1.5,0.2,0 5.0,3.4,1.5,0.2,0
4.4,2.9,1.4,0.2,0 4.4,2.9,1.4,0.2,0
4.9,3.1,1.5,0.1,0 4.9,3.1,1.5,0.1,0
5.4,3.7,1.5,0.2,0 5.4,3.7,1.5,0.2,0
4.8,3.4,1.6,0.2,0 4.8,3.4,1.6,0.2,0
4.8,3.0,1.4,0.1,0 4.8,3.0,1.4,0.1,0
4.3,3.0,1.1,0.1,0 4.3,3.0,1.1,0.1,0
5.8,4.0,1.2,0.2,0 5.8,4.0,1.2,0.2,0
5.7,4.4,1.5,0.4,0 5.7,4.4,1.5,0.4,0
5.4,3.9,1.3,0.4,0 5.4,3.9,1.3,0.4,0
5.1,3.5,1.4,0.3,0 5.1,3.5,1.4,0.3,0
5.7,3.8,1.7,0.3,0 5.7,3.8,1.7,0.3,0
5.1,3.8,1.5,0.3,0 5.1,3.8,1.5,0.3,0
5.4,3.4,1.7,0.2,0 5.4,3.4,1.7,0.2,0
5.1,3.7,1.5,0.4,0 5.1,3.7,1.5,0.4,0
4.6,3.6,1.0,0.2,0 4.6,3.6,1.0,0.2,0
5.1,3.3,1.7,0.5,0 5.1,3.3,1.7,0.5,0
4.8,3.4,1.9,0.2,0 4.8,3.4,1.9,0.2,0
5.0,3.0,1.6,0.2,0 5.0,3.0,1.6,0.2,0
5.0,3.4,1.6,0.4,0 5.0,3.4,1.6,0.4,0
5.2,3.5,1.5,0.2,0 5.2,3.5,1.5,0.2,0
5.2,3.4,1.4,0.2,0 5.2,3.4,1.4,0.2,0
4.7,3.2,1.6,0.2,0 4.7,3.2,1.6,0.2,0
4.8,3.1,1.6,0.2,0 4.8,3.1,1.6,0.2,0
5.4,3.4,1.5,0.4,0 5.4,3.4,1.5,0.4,0
5.2,4.1,1.5,0.1,0 5.2,4.1,1.5,0.1,0
5.5,4.2,1.4,0.2,0 5.5,4.2,1.4,0.2,0
4.9,3.1,1.5,0.2,0 4.9,3.1,1.5,0.2,0
5.0,3.2,1.2,0.2,0 5.0,3.2,1.2,0.2,0
5.5,3.5,1.3,0.2,0 5.5,3.5,1.3,0.2,0
4.9,3.6,1.4,0.1,0 4.9,3.6,1.4,0.1,0
4.4,3.0,1.3,0.2,0 4.4,3.0,1.3,0.2,0
5.1,3.4,1.5,0.2,0 5.1,3.4,1.5,0.2,0
5.0,3.5,1.3,0.3,0 5.0,3.5,1.3,0.3,0
4.5,2.3,1.3,0.3,0 4.5,2.3,1.3,0.3,0
4.4,3.2,1.3,0.2,0 4.4,3.2,1.3,0.2,0
5.0,3.5,1.6,0.6,0 5.0,3.5,1.6,0.6,0
5.1,3.8,1.9,0.4,0 5.1,3.8,1.9,0.4,0
4.8,3.0,1.4,0.3,0 4.8,3.0,1.4,0.3,0
5.1,3.8,1.6,0.2,0 5.1,3.8,1.6,0.2,0
4.6,3.2,1.4,0.2,0 4.6,3.2,1.4,0.2,0
5.3,3.7,1.5,0.2,0 5.3,3.7,1.5,0.2,0
5.0,3.3,1.4,0.2,0 5.0,3.3,1.4,0.2,0
7.0,3.2,4.7,1.4,1 7.0,3.2,4.7,1.4,1
6.4,3.2,4.5,1.5,1 6.4,3.2,4.5,1.5,1
6.9,3.1,4.9,1.5,1 6.9,3.1,4.9,1.5,1
5.5,2.3,4.0,1.3,1 5.5,2.3,4.0,1.3,1
6.5,2.8,4.6,1.5,1 6.5,2.8,4.6,1.5,1
5.7,2.8,4.5,1.3,1 5.7,2.8,4.5,1.3,1
6.3,3.3,4.7,1.6,1 6.3,3.3,4.7,1.6,1
4.9,2.4,3.3,1.0,1 4.9,2.4,3.3,1.0,1
6.6,2.9,4.6,1.3,1 6.6,2.9,4.6,1.3,1
5.2,2.7,3.9,1.4,1 5.2,2.7,3.9,1.4,1
5.0,2.0,3.5,1.0,1 5.0,2.0,3.5,1.0,1
5.9,3.0,4.2,1.5,1 5.9,3.0,4.2,1.5,1
6.0,2.2,4.0,1.0,1 6.0,2.2,4.0,1.0,1
6.1,2.9,4.7,1.4,1 6.1,2.9,4.7,1.4,1
5.6,2.9,3.6,1.3,1 5.6,2.9,3.6,1.3,1
6.7,3.1,4.4,1.4,1 6.7,3.1,4.4,1.4,1
5.6,3.0,4.5,1.5,1 5.6,3.0,4.5,1.5,1
5.8,2.7,4.1,1.0,1 5.8,2.7,4.1,1.0,1
6.2,2.2,4.5,1.5,1 6.2,2.2,4.5,1.5,1
5.6,2.5,3.9,1.1,1 5.6,2.5,3.9,1.1,1
5.9,3.2,4.8,1.8,1 5.9,3.2,4.8,1.8,1
6.1,2.8,4.0,1.3,1 6.1,2.8,4.0,1.3,1
6.3,2.5,4.9,1.5,1 6.3,2.5,4.9,1.5,1
6.1,2.8,4.7,1.2,1 6.1,2.8,4.7,1.2,1
6.4,2.9,4.3,1.3,1 6.4,2.9,4.3,1.3,1
6.6,3.0,4.4,1.4,1 6.6,3.0,4.4,1.4,1
6.8,2.8,4.8,1.4,1 6.8,2.8,4.8,1.4,1
6.7,3.0,5.0,1.7,1 6.7,3.0,5.0,1.7,1
6.0,2.9,4.5,1.5,1 6.0,2.9,4.5,1.5,1
5.7,2.6,3.5,1.0,1 5.7,2.6,3.5,1.0,1
5.5,2.4,3.8,1.1,1 5.5,2.4,3.8,1.1,1
5.5,2.4,3.7,1.0,1 5.5,2.4,3.7,1.0,1
5.8,2.7,3.9,1.2,1 5.8,2.7,3.9,1.2,1
6.0,2.7,5.1,1.6,1 6.0,2.7,5.1,1.6,1
5.4,3.0,4.5,1.5,1 5.4,3.0,4.5,1.5,1
6.0,3.4,4.5,1.6,1 6.0,3.4,4.5,1.6,1
6.7,3.1,4.7,1.5,1 6.7,3.1,4.7,1.5,1
6.3,2.3,4.4,1.3,1 6.3,2.3,4.4,1.3,1
5.6,3.0,4.1,1.3,1 5.6,3.0,4.1,1.3,1
5.5,2.5,4.0,1.3,1 5.5,2.5,4.0,1.3,1
5.5,2.6,4.4,1.2,1 5.5,2.6,4.4,1.2,1
6.1,3.0,4.6,1.4,1 6.1,3.0,4.6,1.4,1
5.8,2.6,4.0,1.2,1 5.8,2.6,4.0,1.2,1
5.0,2.3,3.3,1.0,1 5.0,2.3,3.3,1.0,1
5.6,2.7,4.2,1.3,1 5.6,2.7,4.2,1.3,1
5.7,3.0,4.2,1.2,1 5.7,3.0,4.2,1.2,1
5.7,2.9,4.2,1.3,1 5.7,2.9,4.2,1.3,1
6.2,2.9,4.3,1.3,1 6.2,2.9,4.3,1.3,1
5.1,2.5,3.0,1.1,1 5.1,2.5,3.0,1.1,1
5.7,2.8,4.1,1.3,1 5.7,2.8,4.1,1.3,1
6.3,3.3,6.0,2.5,2 6.3,3.3,6.0,2.5,2
5.8,2.7,5.1,1.9,2 5.8,2.7,5.1,1.9,2
7.1,3.0,5.9,2.1,2 7.1,3.0,5.9,2.1,2
6.3,2.9,5.6,1.8,2 6.3,2.9,5.6,1.8,2
6.5,3.0,5.8,2.2,2 6.5,3.0,5.8,2.2,2
7.6,3.0,6.6,2.1,2 7.6,3.0,6.6,2.1,2
4.9,2.5,4.5,1.7,2 4.9,2.5,4.5,1.7,2
7.3,2.9,6.3,1.8,2 7.3,2.9,6.3,1.8,2
6.7,2.5,5.8,1.8,2 6.7,2.5,5.8,1.8,2
7.2,3.6,6.1,2.5,2 7.2,3.6,6.1,2.5,2
6.5,3.2,5.1,2.0,2 6.5,3.2,5.1,2.0,2
6.4,2.7,5.3,1.9,2 6.4,2.7,5.3,1.9,2
6.8,3.0,5.5,2.1,2 6.8,3.0,5.5,2.1,2
5.7,2.5,5.0,2.0,2 5.7,2.5,5.0,2.0,2
5.8,2.8,5.1,2.4,2 5.8,2.8,5.1,2.4,2
6.4,3.2,5.3,2.3,2 6.4,3.2,5.3,2.3,2
6.5,3.0,5.5,1.8,2 6.5,3.0,5.5,1.8,2
7.7,3.8,6.7,2.2,2 7.7,3.8,6.7,2.2,2
7.7,2.6,6.9,2.3,2 7.7,2.6,6.9,2.3,2
6.0,2.2,5.0,1.5,2 6.0,2.2,5.0,1.5,2
6.9,3.2,5.7,2.3,2 6.9,3.2,5.7,2.3,2
5.6,2.8,4.9,2.0,2 5.6,2.8,4.9,2.0,2
7.7,2.8,6.7,2.0,2 7.7,2.8,6.7,2.0,2
6.3,2.7,4.9,1.8,2 6.3,2.7,4.9,1.8,2
6.7,3.3,5.7,2.1,2 6.7,3.3,5.7,2.1,2
7.2,3.2,6.0,1.8,2 7.2,3.2,6.0,1.8,2
6.2,2.8,4.8,1.8,2 6.2,2.8,4.8,1.8,2
6.1,3.0,4.9,1.8,2 6.1,3.0,4.9,1.8,2
6.4,2.8,5.6,2.1,2 6.4,2.8,5.6,2.1,2
7.2,3.0,5.8,1.6,2 7.2,3.0,5.8,1.6,2
7.4,2.8,6.1,1.9,2 7.4,2.8,6.1,1.9,2
7.9,3.8,6.4,2.0,2 7.9,3.8,6.4,2.0,2
6.4,2.8,5.6,2.2,2 6.4,2.8,5.6,2.2,2
6.3,2.8,5.1,1.5,2 6.3,2.8,5.1,1.5,2
6.1,2.6,5.6,1.4,2 6.1,2.6,5.6,1.4,2
7.7,3.0,6.1,2.3,2 7.7,3.0,6.1,2.3,2
6.3,3.4,5.6,2.4,2 6.3,3.4,5.6,2.4,2
6.4,3.1,5.5,1.8,2 6.4,3.1,5.5,1.8,2
6.0,3.0,4.8,1.8,2 6.0,3.0,4.8,1.8,2
6.9,3.1,5.4,2.1,2 6.9,3.1,5.4,2.1,2
6.7,3.1,5.6,2.4,2 6.7,3.1,5.6,2.4,2
6.9,3.1,5.1,2.3,2 6.9,3.1,5.1,2.3,2
5.8,2.7,5.1,1.9,2 5.8,2.7,5.1,1.9,2
6.8,3.2,5.9,2.3,2 6.8,3.2,5.9,2.3,2
6.7,3.3,5.7,2.5,2 6.7,3.3,5.7,2.5,2
6.7,3.0,5.2,2.3,2 6.7,3.0,5.2,2.3,2
6.3,2.5,5.0,1.9,2 6.3,2.5,5.0,1.9,2
6.5,3.0,5.2,2.0,2 6.5,3.0,5.2,2.0,2
6.2,3.4,5.4,2.3,2 6.2,3.4,5.4,2.3,2
5.9,3.0,5.1,1.8,2 5.9,3.0,5.1,1.8,2
1 150 4 setosa versicolor virginica
2 5.1 3.5 1.4 0.2 0
3 4.9 3.0 1.4 0.2 0
4 4.7 3.2 1.3 0.2 0
5 4.6 3.1 1.5 0.2 0
6 5.0 3.6 1.4 0.2 0
7 5.4 3.9 1.7 0.4 0
8 4.6 3.4 1.4 0.3 0
9 5.0 3.4 1.5 0.2 0
10 4.4 2.9 1.4 0.2 0
11 4.9 3.1 1.5 0.1 0
12 5.4 3.7 1.5 0.2 0
13 4.8 3.4 1.6 0.2 0
14 4.8 3.0 1.4 0.1 0
15 4.3 3.0 1.1 0.1 0
16 5.8 4.0 1.2 0.2 0
17 5.7 4.4 1.5 0.4 0
18 5.4 3.9 1.3 0.4 0
19 5.1 3.5 1.4 0.3 0
20 5.7 3.8 1.7 0.3 0
21 5.1 3.8 1.5 0.3 0
22 5.4 3.4 1.7 0.2 0
23 5.1 3.7 1.5 0.4 0
24 4.6 3.6 1.0 0.2 0
25 5.1 3.3 1.7 0.5 0
26 4.8 3.4 1.9 0.2 0
27 5.0 3.0 1.6 0.2 0
28 5.0 3.4 1.6 0.4 0
29 5.2 3.5 1.5 0.2 0
30 5.2 3.4 1.4 0.2 0
31 4.7 3.2 1.6 0.2 0
32 4.8 3.1 1.6 0.2 0
33 5.4 3.4 1.5 0.4 0
34 5.2 4.1 1.5 0.1 0
35 5.5 4.2 1.4 0.2 0
36 4.9 3.1 1.5 0.2 0
37 5.0 3.2 1.2 0.2 0
38 5.5 3.5 1.3 0.2 0
39 4.9 3.6 1.4 0.1 0
40 4.4 3.0 1.3 0.2 0
41 5.1 3.4 1.5 0.2 0
42 5.0 3.5 1.3 0.3 0
43 4.5 2.3 1.3 0.3 0
44 4.4 3.2 1.3 0.2 0
45 5.0 3.5 1.6 0.6 0
46 5.1 3.8 1.9 0.4 0
47 4.8 3.0 1.4 0.3 0
48 5.1 3.8 1.6 0.2 0
49 4.6 3.2 1.4 0.2 0
50 5.3 3.7 1.5 0.2 0
51 5.0 3.3 1.4 0.2 0
52 7.0 3.2 4.7 1.4 1
53 6.4 3.2 4.5 1.5 1
54 6.9 3.1 4.9 1.5 1
55 5.5 2.3 4.0 1.3 1
56 6.5 2.8 4.6 1.5 1
57 5.7 2.8 4.5 1.3 1
58 6.3 3.3 4.7 1.6 1
59 4.9 2.4 3.3 1.0 1
60 6.6 2.9 4.6 1.3 1
61 5.2 2.7 3.9 1.4 1
62 5.0 2.0 3.5 1.0 1
63 5.9 3.0 4.2 1.5 1
64 6.0 2.2 4.0 1.0 1
65 6.1 2.9 4.7 1.4 1
66 5.6 2.9 3.6 1.3 1
67 6.7 3.1 4.4 1.4 1
68 5.6 3.0 4.5 1.5 1
69 5.8 2.7 4.1 1.0 1
70 6.2 2.2 4.5 1.5 1
71 5.6 2.5 3.9 1.1 1
72 5.9 3.2 4.8 1.8 1
73 6.1 2.8 4.0 1.3 1
74 6.3 2.5 4.9 1.5 1
75 6.1 2.8 4.7 1.2 1
76 6.4 2.9 4.3 1.3 1
77 6.6 3.0 4.4 1.4 1
78 6.8 2.8 4.8 1.4 1
79 6.7 3.0 5.0 1.7 1
80 6.0 2.9 4.5 1.5 1
81 5.7 2.6 3.5 1.0 1
82 5.5 2.4 3.8 1.1 1
83 5.5 2.4 3.7 1.0 1
84 5.8 2.7 3.9 1.2 1
85 6.0 2.7 5.1 1.6 1
86 5.4 3.0 4.5 1.5 1
87 6.0 3.4 4.5 1.6 1
88 6.7 3.1 4.7 1.5 1
89 6.3 2.3 4.4 1.3 1
90 5.6 3.0 4.1 1.3 1
91 5.5 2.5 4.0 1.3 1
92 5.5 2.6 4.4 1.2 1
93 6.1 3.0 4.6 1.4 1
94 5.8 2.6 4.0 1.2 1
95 5.0 2.3 3.3 1.0 1
96 5.6 2.7 4.2 1.3 1
97 5.7 3.0 4.2 1.2 1
98 5.7 2.9 4.2 1.3 1
99 6.2 2.9 4.3 1.3 1
100 5.1 2.5 3.0 1.1 1
101 5.7 2.8 4.1 1.3 1
102 6.3 3.3 6.0 2.5 2
103 5.8 2.7 5.1 1.9 2
104 7.1 3.0 5.9 2.1 2
105 6.3 2.9 5.6 1.8 2
106 6.5 3.0 5.8 2.2 2
107 7.6 3.0 6.6 2.1 2
108 4.9 2.5 4.5 1.7 2
109 7.3 2.9 6.3 1.8 2
110 6.7 2.5 5.8 1.8 2
111 7.2 3.6 6.1 2.5 2
112 6.5 3.2 5.1 2.0 2
113 6.4 2.7 5.3 1.9 2
114 6.8 3.0 5.5 2.1 2
115 5.7 2.5 5.0 2.0 2
116 5.8 2.8 5.1 2.4 2
117 6.4 3.2 5.3 2.3 2
118 6.5 3.0 5.5 1.8 2
119 7.7 3.8 6.7 2.2 2
120 7.7 2.6 6.9 2.3 2
121 6.0 2.2 5.0 1.5 2
122 6.9 3.2 5.7 2.3 2
123 5.6 2.8 4.9 2.0 2
124 7.7 2.8 6.7 2.0 2
125 6.3 2.7 4.9 1.8 2
126 6.7 3.3 5.7 2.1 2
127 7.2 3.2 6.0 1.8 2
128 6.2 2.8 4.8 1.8 2
129 6.1 3.0 4.9 1.8 2
130 6.4 2.8 5.6 2.1 2
131 7.2 3.0 5.8 1.6 2
132 7.4 2.8 6.1 1.9 2
133 7.9 3.8 6.4 2.0 2
134 6.4 2.8 5.6 2.2 2
135 6.3 2.8 5.1 1.5 2
136 6.1 2.6 5.6 1.4 2
137 7.7 3.0 6.1 2.3 2
138 6.3 3.4 5.6 2.4 2
139 6.4 3.1 5.5 1.8 2
140 6.0 3.0 4.8 1.8 2
141 6.9 3.1 5.4 2.1 2
142 6.7 3.1 5.6 2.4 2
143 6.9 3.1 5.1 2.3 2
144 5.8 2.7 5.1 1.9 2
145 6.8 3.2 5.9 2.3 2
146 6.7 3.3 5.7 2.5 2
147 6.7 3.0 5.2 2.3 2
148 6.3 2.5 5.0 1.9 2
149 6.5 3.0 5.2 2.0 2
150 6.2 3.4 5.4 2.3 2
151 5.9 3.0 5.1 1.8 2
@@ -1,21 +1,21 @@
Chins Situps Jumps Chins Situps Jumps
5 162 60 5 162 60
2 110 60 2 110 60
12 101 101 12 101 101
12 105 37 12 105 37
13 155 58 13 155 58
4 101 42 4 101 42
8 101 38 8 101 38
6 125 40 6 125 40
15 200 40 15 200 40
17 251 250 17 251 250
17 120 38 17 120 38
13 210 115 13 210 115
14 215 105 14 215 105
1 50 50 1 50 50
6 70 31 6 70 31
12 210 120 12 210 120
4 60 25 4 60 25
11 230 80 11 230 80
15 225 73 15 225 73
2 110 43 2 110 43
1 Chins Situps Jumps
2 5 162 60
3 2 110 60
4 12 101 101
5 12 105 37
6 13 155 58
7 4 101 42
8 8 101 38
9 6 125 40
10 15 200 40
11 17 251 250
12 17 120 38
13 13 210 115
14 14 215 105
15 1 50 50
16 6 70 31
17 12 210 120
18 4 60 25
19 11 230 80
20 15 225 73
21 2 110 43
@@ -1,21 +1,21 @@
Weight Waist Pulse Weight Waist Pulse
191 36 50 191 36 50
189 37 52 189 37 52
193 38 58 193 38 58
162 35 62 162 35 62
189 35 46 189 35 46
182 36 56 182 36 56
211 38 56 211 38 56
167 34 60 167 34 60
176 31 74 176 31 74
154 33 56 154 33 56
169 34 50 169 34 50
166 33 52 166 33 52
154 34 64 154 34 64
247 46 50 247 46 50
193 36 46 193 36 46
202 37 62 202 37 62
176 37 54 176 37 54
157 32 52 157 32 52
156 33 54 156 33 54
138 33 68 138 33 68
1 Weight Waist Pulse
2 191 36 50
3 189 37 52
4 193 38 58
5 162 35 62
6 189 35 46
7 182 36 56
8 211 38 56
9 167 34 60
10 176 31 74
11 154 33 56
12 169 34 50
13 166 33 52
14 154 34 64
15 247 46 50
16 193 36 46
17 202 37 62
18 176 37 54
19 157 32 52
20 156 33 54
21 138 33 68
@@ -1,179 +1,179 @@
178,13,class_0,class_1,class_2 178,13,class_0,class_1,class_2
14.23,1.71,2.43,15.6,127,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065,0 14.23,1.71,2.43,15.6,127,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065,0
13.2,1.78,2.14,11.2,100,2.65,2.76,0.26,1.28,4.38,1.05,3.4,1050,0 13.2,1.78,2.14,11.2,100,2.65,2.76,0.26,1.28,4.38,1.05,3.4,1050,0
13.16,2.36,2.67,18.6,101,2.8,3.24,0.3,2.81,5.68,1.03,3.17,1185,0 13.16,2.36,2.67,18.6,101,2.8,3.24,0.3,2.81,5.68,1.03,3.17,1185,0
14.37,1.95,2.5,16.8,113,3.85,3.49,0.24,2.18,7.8,0.86,3.45,1480,0 14.37,1.95,2.5,16.8,113,3.85,3.49,0.24,2.18,7.8,0.86,3.45,1480,0
13.24,2.59,2.87,21,118,2.8,2.69,0.39,1.82,4.32,1.04,2.93,735,0 13.24,2.59,2.87,21,118,2.8,2.69,0.39,1.82,4.32,1.04,2.93,735,0
14.2,1.76,2.45,15.2,112,3.27,3.39,0.34,1.97,6.75,1.05,2.85,1450,0 14.2,1.76,2.45,15.2,112,3.27,3.39,0.34,1.97,6.75,1.05,2.85,1450,0
14.39,1.87,2.45,14.6,96,2.5,2.52,0.3,1.98,5.25,1.02,3.58,1290,0 14.39,1.87,2.45,14.6,96,2.5,2.52,0.3,1.98,5.25,1.02,3.58,1290,0
14.06,2.15,2.61,17.6,121,2.6,2.51,0.31,1.25,5.05,1.06,3.58,1295,0 14.06,2.15,2.61,17.6,121,2.6,2.51,0.31,1.25,5.05,1.06,3.58,1295,0
14.83,1.64,2.17,14,97,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045,0 14.83,1.64,2.17,14,97,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045,0
13.86,1.35,2.27,16,98,2.98,3.15,0.22,1.85,7.22,1.01,3.55,1045,0 13.86,1.35,2.27,16,98,2.98,3.15,0.22,1.85,7.22,1.01,3.55,1045,0
14.1,2.16,2.3,18,105,2.95,3.32,0.22,2.38,5.75,1.25,3.17,1510,0 14.1,2.16,2.3,18,105,2.95,3.32,0.22,2.38,5.75,1.25,3.17,1510,0
14.12,1.48,2.32,16.8,95,2.2,2.43,0.26,1.57,5,1.17,2.82,1280,0 14.12,1.48,2.32,16.8,95,2.2,2.43,0.26,1.57,5,1.17,2.82,1280,0
13.75,1.73,2.41,16,89,2.6,2.76,0.29,1.81,5.6,1.15,2.9,1320,0 13.75,1.73,2.41,16,89,2.6,2.76,0.29,1.81,5.6,1.15,2.9,1320,0
14.75,1.73,2.39,11.4,91,3.1,3.69,0.43,2.81,5.4,1.25,2.73,1150,0 14.75,1.73,2.39,11.4,91,3.1,3.69,0.43,2.81,5.4,1.25,2.73,1150,0
14.38,1.87,2.38,12,102,3.3,3.64,0.29,2.96,7.5,1.2,3,1547,0 14.38,1.87,2.38,12,102,3.3,3.64,0.29,2.96,7.5,1.2,3,1547,0
13.63,1.81,2.7,17.2,112,2.85,2.91,0.3,1.46,7.3,1.28,2.88,1310,0 13.63,1.81,2.7,17.2,112,2.85,2.91,0.3,1.46,7.3,1.28,2.88,1310,0
14.3,1.92,2.72,20,120,2.8,3.14,0.33,1.97,6.2,1.07,2.65,1280,0 14.3,1.92,2.72,20,120,2.8,3.14,0.33,1.97,6.2,1.07,2.65,1280,0
13.83,1.57,2.62,20,115,2.95,3.4,0.4,1.72,6.6,1.13,2.57,1130,0 13.83,1.57,2.62,20,115,2.95,3.4,0.4,1.72,6.6,1.13,2.57,1130,0
14.19,1.59,2.48,16.5,108,3.3,3.93,0.32,1.86,8.7,1.23,2.82,1680,0 14.19,1.59,2.48,16.5,108,3.3,3.93,0.32,1.86,8.7,1.23,2.82,1680,0
13.64,3.1,2.56,15.2,116,2.7,3.03,0.17,1.66,5.1,0.96,3.36,845,0 13.64,3.1,2.56,15.2,116,2.7,3.03,0.17,1.66,5.1,0.96,3.36,845,0
14.06,1.63,2.28,16,126,3,3.17,0.24,2.1,5.65,1.09,3.71,780,0 14.06,1.63,2.28,16,126,3,3.17,0.24,2.1,5.65,1.09,3.71,780,0
12.93,3.8,2.65,18.6,102,2.41,2.41,0.25,1.98,4.5,1.03,3.52,770,0 12.93,3.8,2.65,18.6,102,2.41,2.41,0.25,1.98,4.5,1.03,3.52,770,0
13.71,1.86,2.36,16.6,101,2.61,2.88,0.27,1.69,3.8,1.11,4,1035,0 13.71,1.86,2.36,16.6,101,2.61,2.88,0.27,1.69,3.8,1.11,4,1035,0
12.85,1.6,2.52,17.8,95,2.48,2.37,0.26,1.46,3.93,1.09,3.63,1015,0 12.85,1.6,2.52,17.8,95,2.48,2.37,0.26,1.46,3.93,1.09,3.63,1015,0
13.5,1.81,2.61,20,96,2.53,2.61,0.28,1.66,3.52,1.12,3.82,845,0 13.5,1.81,2.61,20,96,2.53,2.61,0.28,1.66,3.52,1.12,3.82,845,0
13.05,2.05,3.22,25,124,2.63,2.68,0.47,1.92,3.58,1.13,3.2,830,0 13.05,2.05,3.22,25,124,2.63,2.68,0.47,1.92,3.58,1.13,3.2,830,0
13.39,1.77,2.62,16.1,93,2.85,2.94,0.34,1.45,4.8,0.92,3.22,1195,0 13.39,1.77,2.62,16.1,93,2.85,2.94,0.34,1.45,4.8,0.92,3.22,1195,0
13.3,1.72,2.14,17,94,2.4,2.19,0.27,1.35,3.95,1.02,2.77,1285,0 13.3,1.72,2.14,17,94,2.4,2.19,0.27,1.35,3.95,1.02,2.77,1285,0
13.87,1.9,2.8,19.4,107,2.95,2.97,0.37,1.76,4.5,1.25,3.4,915,0 13.87,1.9,2.8,19.4,107,2.95,2.97,0.37,1.76,4.5,1.25,3.4,915,0
14.02,1.68,2.21,16,96,2.65,2.33,0.26,1.98,4.7,1.04,3.59,1035,0 14.02,1.68,2.21,16,96,2.65,2.33,0.26,1.98,4.7,1.04,3.59,1035,0
13.73,1.5,2.7,22.5,101,3,3.25,0.29,2.38,5.7,1.19,2.71,1285,0 13.73,1.5,2.7,22.5,101,3,3.25,0.29,2.38,5.7,1.19,2.71,1285,0
13.58,1.66,2.36,19.1,106,2.86,3.19,0.22,1.95,6.9,1.09,2.88,1515,0 13.58,1.66,2.36,19.1,106,2.86,3.19,0.22,1.95,6.9,1.09,2.88,1515,0
13.68,1.83,2.36,17.2,104,2.42,2.69,0.42,1.97,3.84,1.23,2.87,990,0 13.68,1.83,2.36,17.2,104,2.42,2.69,0.42,1.97,3.84,1.23,2.87,990,0
13.76,1.53,2.7,19.5,132,2.95,2.74,0.5,1.35,5.4,1.25,3,1235,0 13.76,1.53,2.7,19.5,132,2.95,2.74,0.5,1.35,5.4,1.25,3,1235,0
13.51,1.8,2.65,19,110,2.35,2.53,0.29,1.54,4.2,1.1,2.87,1095,0 13.51,1.8,2.65,19,110,2.35,2.53,0.29,1.54,4.2,1.1,2.87,1095,0
13.48,1.81,2.41,20.5,100,2.7,2.98,0.26,1.86,5.1,1.04,3.47,920,0 13.48,1.81,2.41,20.5,100,2.7,2.98,0.26,1.86,5.1,1.04,3.47,920,0
13.28,1.64,2.84,15.5,110,2.6,2.68,0.34,1.36,4.6,1.09,2.78,880,0 13.28,1.64,2.84,15.5,110,2.6,2.68,0.34,1.36,4.6,1.09,2.78,880,0
13.05,1.65,2.55,18,98,2.45,2.43,0.29,1.44,4.25,1.12,2.51,1105,0 13.05,1.65,2.55,18,98,2.45,2.43,0.29,1.44,4.25,1.12,2.51,1105,0
13.07,1.5,2.1,15.5,98,2.4,2.64,0.28,1.37,3.7,1.18,2.69,1020,0 13.07,1.5,2.1,15.5,98,2.4,2.64,0.28,1.37,3.7,1.18,2.69,1020,0
14.22,3.99,2.51,13.2,128,3,3.04,0.2,2.08,5.1,0.89,3.53,760,0 14.22,3.99,2.51,13.2,128,3,3.04,0.2,2.08,5.1,0.89,3.53,760,0
13.56,1.71,2.31,16.2,117,3.15,3.29,0.34,2.34,6.13,0.95,3.38,795,0 13.56,1.71,2.31,16.2,117,3.15,3.29,0.34,2.34,6.13,0.95,3.38,795,0
13.41,3.84,2.12,18.8,90,2.45,2.68,0.27,1.48,4.28,0.91,3,1035,0 13.41,3.84,2.12,18.8,90,2.45,2.68,0.27,1.48,4.28,0.91,3,1035,0
13.88,1.89,2.59,15,101,3.25,3.56,0.17,1.7,5.43,0.88,3.56,1095,0 13.88,1.89,2.59,15,101,3.25,3.56,0.17,1.7,5.43,0.88,3.56,1095,0
13.24,3.98,2.29,17.5,103,2.64,2.63,0.32,1.66,4.36,0.82,3,680,0 13.24,3.98,2.29,17.5,103,2.64,2.63,0.32,1.66,4.36,0.82,3,680,0
13.05,1.77,2.1,17,107,3,3,0.28,2.03,5.04,0.88,3.35,885,0 13.05,1.77,2.1,17,107,3,3,0.28,2.03,5.04,0.88,3.35,885,0
14.21,4.04,2.44,18.9,111,2.85,2.65,0.3,1.25,5.24,0.87,3.33,1080,0 14.21,4.04,2.44,18.9,111,2.85,2.65,0.3,1.25,5.24,0.87,3.33,1080,0
14.38,3.59,2.28,16,102,3.25,3.17,0.27,2.19,4.9,1.04,3.44,1065,0 14.38,3.59,2.28,16,102,3.25,3.17,0.27,2.19,4.9,1.04,3.44,1065,0
13.9,1.68,2.12,16,101,3.1,3.39,0.21,2.14,6.1,0.91,3.33,985,0 13.9,1.68,2.12,16,101,3.1,3.39,0.21,2.14,6.1,0.91,3.33,985,0
14.1,2.02,2.4,18.8,103,2.75,2.92,0.32,2.38,6.2,1.07,2.75,1060,0 14.1,2.02,2.4,18.8,103,2.75,2.92,0.32,2.38,6.2,1.07,2.75,1060,0
13.94,1.73,2.27,17.4,108,2.88,3.54,0.32,2.08,8.9,1.12,3.1,1260,0 13.94,1.73,2.27,17.4,108,2.88,3.54,0.32,2.08,8.9,1.12,3.1,1260,0
13.05,1.73,2.04,12.4,92,2.72,3.27,0.17,2.91,7.2,1.12,2.91,1150,0 13.05,1.73,2.04,12.4,92,2.72,3.27,0.17,2.91,7.2,1.12,2.91,1150,0
13.83,1.65,2.6,17.2,94,2.45,2.99,0.22,2.29,5.6,1.24,3.37,1265,0 13.83,1.65,2.6,17.2,94,2.45,2.99,0.22,2.29,5.6,1.24,3.37,1265,0
13.82,1.75,2.42,14,111,3.88,3.74,0.32,1.87,7.05,1.01,3.26,1190,0 13.82,1.75,2.42,14,111,3.88,3.74,0.32,1.87,7.05,1.01,3.26,1190,0
13.77,1.9,2.68,17.1,115,3,2.79,0.39,1.68,6.3,1.13,2.93,1375,0 13.77,1.9,2.68,17.1,115,3,2.79,0.39,1.68,6.3,1.13,2.93,1375,0
13.74,1.67,2.25,16.4,118,2.6,2.9,0.21,1.62,5.85,0.92,3.2,1060,0 13.74,1.67,2.25,16.4,118,2.6,2.9,0.21,1.62,5.85,0.92,3.2,1060,0
13.56,1.73,2.46,20.5,116,2.96,2.78,0.2,2.45,6.25,0.98,3.03,1120,0 13.56,1.73,2.46,20.5,116,2.96,2.78,0.2,2.45,6.25,0.98,3.03,1120,0
14.22,1.7,2.3,16.3,118,3.2,3,0.26,2.03,6.38,0.94,3.31,970,0 14.22,1.7,2.3,16.3,118,3.2,3,0.26,2.03,6.38,0.94,3.31,970,0
13.29,1.97,2.68,16.8,102,3,3.23,0.31,1.66,6,1.07,2.84,1270,0 13.29,1.97,2.68,16.8,102,3,3.23,0.31,1.66,6,1.07,2.84,1270,0
13.72,1.43,2.5,16.7,108,3.4,3.67,0.19,2.04,6.8,0.89,2.87,1285,0 13.72,1.43,2.5,16.7,108,3.4,3.67,0.19,2.04,6.8,0.89,2.87,1285,0
12.37,0.94,1.36,10.6,88,1.98,0.57,0.28,0.42,1.95,1.05,1.82,520,1 12.37,0.94,1.36,10.6,88,1.98,0.57,0.28,0.42,1.95,1.05,1.82,520,1
12.33,1.1,2.28,16,101,2.05,1.09,0.63,0.41,3.27,1.25,1.67,680,1 12.33,1.1,2.28,16,101,2.05,1.09,0.63,0.41,3.27,1.25,1.67,680,1
12.64,1.36,2.02,16.8,100,2.02,1.41,0.53,0.62,5.75,0.98,1.59,450,1 12.64,1.36,2.02,16.8,100,2.02,1.41,0.53,0.62,5.75,0.98,1.59,450,1
13.67,1.25,1.92,18,94,2.1,1.79,0.32,0.73,3.8,1.23,2.46,630,1 13.67,1.25,1.92,18,94,2.1,1.79,0.32,0.73,3.8,1.23,2.46,630,1
12.37,1.13,2.16,19,87,3.5,3.1,0.19,1.87,4.45,1.22,2.87,420,1 12.37,1.13,2.16,19,87,3.5,3.1,0.19,1.87,4.45,1.22,2.87,420,1
12.17,1.45,2.53,19,104,1.89,1.75,0.45,1.03,2.95,1.45,2.23,355,1 12.17,1.45,2.53,19,104,1.89,1.75,0.45,1.03,2.95,1.45,2.23,355,1
12.37,1.21,2.56,18.1,98,2.42,2.65,0.37,2.08,4.6,1.19,2.3,678,1 12.37,1.21,2.56,18.1,98,2.42,2.65,0.37,2.08,4.6,1.19,2.3,678,1
13.11,1.01,1.7,15,78,2.98,3.18,0.26,2.28,5.3,1.12,3.18,502,1 13.11,1.01,1.7,15,78,2.98,3.18,0.26,2.28,5.3,1.12,3.18,502,1
12.37,1.17,1.92,19.6,78,2.11,2,0.27,1.04,4.68,1.12,3.48,510,1 12.37,1.17,1.92,19.6,78,2.11,2,0.27,1.04,4.68,1.12,3.48,510,1
13.34,0.94,2.36,17,110,2.53,1.3,0.55,0.42,3.17,1.02,1.93,750,1 13.34,0.94,2.36,17,110,2.53,1.3,0.55,0.42,3.17,1.02,1.93,750,1
12.21,1.19,1.75,16.8,151,1.85,1.28,0.14,2.5,2.85,1.28,3.07,718,1 12.21,1.19,1.75,16.8,151,1.85,1.28,0.14,2.5,2.85,1.28,3.07,718,1
12.29,1.61,2.21,20.4,103,1.1,1.02,0.37,1.46,3.05,0.906,1.82,870,1 12.29,1.61,2.21,20.4,103,1.1,1.02,0.37,1.46,3.05,0.906,1.82,870,1
13.86,1.51,2.67,25,86,2.95,2.86,0.21,1.87,3.38,1.36,3.16,410,1 13.86,1.51,2.67,25,86,2.95,2.86,0.21,1.87,3.38,1.36,3.16,410,1
13.49,1.66,2.24,24,87,1.88,1.84,0.27,1.03,3.74,0.98,2.78,472,1 13.49,1.66,2.24,24,87,1.88,1.84,0.27,1.03,3.74,0.98,2.78,472,1
12.99,1.67,2.6,30,139,3.3,2.89,0.21,1.96,3.35,1.31,3.5,985,1 12.99,1.67,2.6,30,139,3.3,2.89,0.21,1.96,3.35,1.31,3.5,985,1
11.96,1.09,2.3,21,101,3.38,2.14,0.13,1.65,3.21,0.99,3.13,886,1 11.96,1.09,2.3,21,101,3.38,2.14,0.13,1.65,3.21,0.99,3.13,886,1
11.66,1.88,1.92,16,97,1.61,1.57,0.34,1.15,3.8,1.23,2.14,428,1 11.66,1.88,1.92,16,97,1.61,1.57,0.34,1.15,3.8,1.23,2.14,428,1
13.03,0.9,1.71,16,86,1.95,2.03,0.24,1.46,4.6,1.19,2.48,392,1 13.03,0.9,1.71,16,86,1.95,2.03,0.24,1.46,4.6,1.19,2.48,392,1
11.84,2.89,2.23,18,112,1.72,1.32,0.43,0.95,2.65,0.96,2.52,500,1 11.84,2.89,2.23,18,112,1.72,1.32,0.43,0.95,2.65,0.96,2.52,500,1
12.33,0.99,1.95,14.8,136,1.9,1.85,0.35,2.76,3.4,1.06,2.31,750,1 12.33,0.99,1.95,14.8,136,1.9,1.85,0.35,2.76,3.4,1.06,2.31,750,1
12.7,3.87,2.4,23,101,2.83,2.55,0.43,1.95,2.57,1.19,3.13,463,1 12.7,3.87,2.4,23,101,2.83,2.55,0.43,1.95,2.57,1.19,3.13,463,1
12,0.92,2,19,86,2.42,2.26,0.3,1.43,2.5,1.38,3.12,278,1 12,0.92,2,19,86,2.42,2.26,0.3,1.43,2.5,1.38,3.12,278,1
12.72,1.81,2.2,18.8,86,2.2,2.53,0.26,1.77,3.9,1.16,3.14,714,1 12.72,1.81,2.2,18.8,86,2.2,2.53,0.26,1.77,3.9,1.16,3.14,714,1
12.08,1.13,2.51,24,78,2,1.58,0.4,1.4,2.2,1.31,2.72,630,1 12.08,1.13,2.51,24,78,2,1.58,0.4,1.4,2.2,1.31,2.72,630,1
13.05,3.86,2.32,22.5,85,1.65,1.59,0.61,1.62,4.8,0.84,2.01,515,1 13.05,3.86,2.32,22.5,85,1.65,1.59,0.61,1.62,4.8,0.84,2.01,515,1
11.84,0.89,2.58,18,94,2.2,2.21,0.22,2.35,3.05,0.79,3.08,520,1 11.84,0.89,2.58,18,94,2.2,2.21,0.22,2.35,3.05,0.79,3.08,520,1
12.67,0.98,2.24,18,99,2.2,1.94,0.3,1.46,2.62,1.23,3.16,450,1 12.67,0.98,2.24,18,99,2.2,1.94,0.3,1.46,2.62,1.23,3.16,450,1
12.16,1.61,2.31,22.8,90,1.78,1.69,0.43,1.56,2.45,1.33,2.26,495,1 12.16,1.61,2.31,22.8,90,1.78,1.69,0.43,1.56,2.45,1.33,2.26,495,1
11.65,1.67,2.62,26,88,1.92,1.61,0.4,1.34,2.6,1.36,3.21,562,1 11.65,1.67,2.62,26,88,1.92,1.61,0.4,1.34,2.6,1.36,3.21,562,1
11.64,2.06,2.46,21.6,84,1.95,1.69,0.48,1.35,2.8,1,2.75,680,1 11.64,2.06,2.46,21.6,84,1.95,1.69,0.48,1.35,2.8,1,2.75,680,1
12.08,1.33,2.3,23.6,70,2.2,1.59,0.42,1.38,1.74,1.07,3.21,625,1 12.08,1.33,2.3,23.6,70,2.2,1.59,0.42,1.38,1.74,1.07,3.21,625,1
12.08,1.83,2.32,18.5,81,1.6,1.5,0.52,1.64,2.4,1.08,2.27,480,1 12.08,1.83,2.32,18.5,81,1.6,1.5,0.52,1.64,2.4,1.08,2.27,480,1
12,1.51,2.42,22,86,1.45,1.25,0.5,1.63,3.6,1.05,2.65,450,1 12,1.51,2.42,22,86,1.45,1.25,0.5,1.63,3.6,1.05,2.65,450,1
12.69,1.53,2.26,20.7,80,1.38,1.46,0.58,1.62,3.05,0.96,2.06,495,1 12.69,1.53,2.26,20.7,80,1.38,1.46,0.58,1.62,3.05,0.96,2.06,495,1
12.29,2.83,2.22,18,88,2.45,2.25,0.25,1.99,2.15,1.15,3.3,290,1 12.29,2.83,2.22,18,88,2.45,2.25,0.25,1.99,2.15,1.15,3.3,290,1
11.62,1.99,2.28,18,98,3.02,2.26,0.17,1.35,3.25,1.16,2.96,345,1 11.62,1.99,2.28,18,98,3.02,2.26,0.17,1.35,3.25,1.16,2.96,345,1
12.47,1.52,2.2,19,162,2.5,2.27,0.32,3.28,2.6,1.16,2.63,937,1 12.47,1.52,2.2,19,162,2.5,2.27,0.32,3.28,2.6,1.16,2.63,937,1
11.81,2.12,2.74,21.5,134,1.6,0.99,0.14,1.56,2.5,0.95,2.26,625,1 11.81,2.12,2.74,21.5,134,1.6,0.99,0.14,1.56,2.5,0.95,2.26,625,1
12.29,1.41,1.98,16,85,2.55,2.5,0.29,1.77,2.9,1.23,2.74,428,1 12.29,1.41,1.98,16,85,2.55,2.5,0.29,1.77,2.9,1.23,2.74,428,1
12.37,1.07,2.1,18.5,88,3.52,3.75,0.24,1.95,4.5,1.04,2.77,660,1 12.37,1.07,2.1,18.5,88,3.52,3.75,0.24,1.95,4.5,1.04,2.77,660,1
12.29,3.17,2.21,18,88,2.85,2.99,0.45,2.81,2.3,1.42,2.83,406,1 12.29,3.17,2.21,18,88,2.85,2.99,0.45,2.81,2.3,1.42,2.83,406,1
12.08,2.08,1.7,17.5,97,2.23,2.17,0.26,1.4,3.3,1.27,2.96,710,1 12.08,2.08,1.7,17.5,97,2.23,2.17,0.26,1.4,3.3,1.27,2.96,710,1
12.6,1.34,1.9,18.5,88,1.45,1.36,0.29,1.35,2.45,1.04,2.77,562,1 12.6,1.34,1.9,18.5,88,1.45,1.36,0.29,1.35,2.45,1.04,2.77,562,1
12.34,2.45,2.46,21,98,2.56,2.11,0.34,1.31,2.8,0.8,3.38,438,1 12.34,2.45,2.46,21,98,2.56,2.11,0.34,1.31,2.8,0.8,3.38,438,1
11.82,1.72,1.88,19.5,86,2.5,1.64,0.37,1.42,2.06,0.94,2.44,415,1 11.82,1.72,1.88,19.5,86,2.5,1.64,0.37,1.42,2.06,0.94,2.44,415,1
12.51,1.73,1.98,20.5,85,2.2,1.92,0.32,1.48,2.94,1.04,3.57,672,1 12.51,1.73,1.98,20.5,85,2.2,1.92,0.32,1.48,2.94,1.04,3.57,672,1
12.42,2.55,2.27,22,90,1.68,1.84,0.66,1.42,2.7,0.86,3.3,315,1 12.42,2.55,2.27,22,90,1.68,1.84,0.66,1.42,2.7,0.86,3.3,315,1
12.25,1.73,2.12,19,80,1.65,2.03,0.37,1.63,3.4,1,3.17,510,1 12.25,1.73,2.12,19,80,1.65,2.03,0.37,1.63,3.4,1,3.17,510,1
12.72,1.75,2.28,22.5,84,1.38,1.76,0.48,1.63,3.3,0.88,2.42,488,1 12.72,1.75,2.28,22.5,84,1.38,1.76,0.48,1.63,3.3,0.88,2.42,488,1
12.22,1.29,1.94,19,92,2.36,2.04,0.39,2.08,2.7,0.86,3.02,312,1 12.22,1.29,1.94,19,92,2.36,2.04,0.39,2.08,2.7,0.86,3.02,312,1
11.61,1.35,2.7,20,94,2.74,2.92,0.29,2.49,2.65,0.96,3.26,680,1 11.61,1.35,2.7,20,94,2.74,2.92,0.29,2.49,2.65,0.96,3.26,680,1
11.46,3.74,1.82,19.5,107,3.18,2.58,0.24,3.58,2.9,0.75,2.81,562,1 11.46,3.74,1.82,19.5,107,3.18,2.58,0.24,3.58,2.9,0.75,2.81,562,1
12.52,2.43,2.17,21,88,2.55,2.27,0.26,1.22,2,0.9,2.78,325,1 12.52,2.43,2.17,21,88,2.55,2.27,0.26,1.22,2,0.9,2.78,325,1
11.76,2.68,2.92,20,103,1.75,2.03,0.6,1.05,3.8,1.23,2.5,607,1 11.76,2.68,2.92,20,103,1.75,2.03,0.6,1.05,3.8,1.23,2.5,607,1
11.41,0.74,2.5,21,88,2.48,2.01,0.42,1.44,3.08,1.1,2.31,434,1 11.41,0.74,2.5,21,88,2.48,2.01,0.42,1.44,3.08,1.1,2.31,434,1
12.08,1.39,2.5,22.5,84,2.56,2.29,0.43,1.04,2.9,0.93,3.19,385,1 12.08,1.39,2.5,22.5,84,2.56,2.29,0.43,1.04,2.9,0.93,3.19,385,1
11.03,1.51,2.2,21.5,85,2.46,2.17,0.52,2.01,1.9,1.71,2.87,407,1 11.03,1.51,2.2,21.5,85,2.46,2.17,0.52,2.01,1.9,1.71,2.87,407,1
11.82,1.47,1.99,20.8,86,1.98,1.6,0.3,1.53,1.95,0.95,3.33,495,1 11.82,1.47,1.99,20.8,86,1.98,1.6,0.3,1.53,1.95,0.95,3.33,495,1
12.42,1.61,2.19,22.5,108,2,2.09,0.34,1.61,2.06,1.06,2.96,345,1 12.42,1.61,2.19,22.5,108,2,2.09,0.34,1.61,2.06,1.06,2.96,345,1
12.77,3.43,1.98,16,80,1.63,1.25,0.43,0.83,3.4,0.7,2.12,372,1 12.77,3.43,1.98,16,80,1.63,1.25,0.43,0.83,3.4,0.7,2.12,372,1
12,3.43,2,19,87,2,1.64,0.37,1.87,1.28,0.93,3.05,564,1 12,3.43,2,19,87,2,1.64,0.37,1.87,1.28,0.93,3.05,564,1
11.45,2.4,2.42,20,96,2.9,2.79,0.32,1.83,3.25,0.8,3.39,625,1 11.45,2.4,2.42,20,96,2.9,2.79,0.32,1.83,3.25,0.8,3.39,625,1
11.56,2.05,3.23,28.5,119,3.18,5.08,0.47,1.87,6,0.93,3.69,465,1 11.56,2.05,3.23,28.5,119,3.18,5.08,0.47,1.87,6,0.93,3.69,465,1
12.42,4.43,2.73,26.5,102,2.2,2.13,0.43,1.71,2.08,0.92,3.12,365,1 12.42,4.43,2.73,26.5,102,2.2,2.13,0.43,1.71,2.08,0.92,3.12,365,1
13.05,5.8,2.13,21.5,86,2.62,2.65,0.3,2.01,2.6,0.73,3.1,380,1 13.05,5.8,2.13,21.5,86,2.62,2.65,0.3,2.01,2.6,0.73,3.1,380,1
11.87,4.31,2.39,21,82,2.86,3.03,0.21,2.91,2.8,0.75,3.64,380,1 11.87,4.31,2.39,21,82,2.86,3.03,0.21,2.91,2.8,0.75,3.64,380,1
12.07,2.16,2.17,21,85,2.6,2.65,0.37,1.35,2.76,0.86,3.28,378,1 12.07,2.16,2.17,21,85,2.6,2.65,0.37,1.35,2.76,0.86,3.28,378,1
12.43,1.53,2.29,21.5,86,2.74,3.15,0.39,1.77,3.94,0.69,2.84,352,1 12.43,1.53,2.29,21.5,86,2.74,3.15,0.39,1.77,3.94,0.69,2.84,352,1
11.79,2.13,2.78,28.5,92,2.13,2.24,0.58,1.76,3,0.97,2.44,466,1 11.79,2.13,2.78,28.5,92,2.13,2.24,0.58,1.76,3,0.97,2.44,466,1
12.37,1.63,2.3,24.5,88,2.22,2.45,0.4,1.9,2.12,0.89,2.78,342,1 12.37,1.63,2.3,24.5,88,2.22,2.45,0.4,1.9,2.12,0.89,2.78,342,1
12.04,4.3,2.38,22,80,2.1,1.75,0.42,1.35,2.6,0.79,2.57,580,1 12.04,4.3,2.38,22,80,2.1,1.75,0.42,1.35,2.6,0.79,2.57,580,1
12.86,1.35,2.32,18,122,1.51,1.25,0.21,0.94,4.1,0.76,1.29,630,2 12.86,1.35,2.32,18,122,1.51,1.25,0.21,0.94,4.1,0.76,1.29,630,2
12.88,2.99,2.4,20,104,1.3,1.22,0.24,0.83,5.4,0.74,1.42,530,2 12.88,2.99,2.4,20,104,1.3,1.22,0.24,0.83,5.4,0.74,1.42,530,2
12.81,2.31,2.4,24,98,1.15,1.09,0.27,0.83,5.7,0.66,1.36,560,2 12.81,2.31,2.4,24,98,1.15,1.09,0.27,0.83,5.7,0.66,1.36,560,2
12.7,3.55,2.36,21.5,106,1.7,1.2,0.17,0.84,5,0.78,1.29,600,2 12.7,3.55,2.36,21.5,106,1.7,1.2,0.17,0.84,5,0.78,1.29,600,2
12.51,1.24,2.25,17.5,85,2,0.58,0.6,1.25,5.45,0.75,1.51,650,2 12.51,1.24,2.25,17.5,85,2,0.58,0.6,1.25,5.45,0.75,1.51,650,2
12.6,2.46,2.2,18.5,94,1.62,0.66,0.63,0.94,7.1,0.73,1.58,695,2 12.6,2.46,2.2,18.5,94,1.62,0.66,0.63,0.94,7.1,0.73,1.58,695,2
12.25,4.72,2.54,21,89,1.38,0.47,0.53,0.8,3.85,0.75,1.27,720,2 12.25,4.72,2.54,21,89,1.38,0.47,0.53,0.8,3.85,0.75,1.27,720,2
12.53,5.51,2.64,25,96,1.79,0.6,0.63,1.1,5,0.82,1.69,515,2 12.53,5.51,2.64,25,96,1.79,0.6,0.63,1.1,5,0.82,1.69,515,2
13.49,3.59,2.19,19.5,88,1.62,0.48,0.58,0.88,5.7,0.81,1.82,580,2 13.49,3.59,2.19,19.5,88,1.62,0.48,0.58,0.88,5.7,0.81,1.82,580,2
12.84,2.96,2.61,24,101,2.32,0.6,0.53,0.81,4.92,0.89,2.15,590,2 12.84,2.96,2.61,24,101,2.32,0.6,0.53,0.81,4.92,0.89,2.15,590,2
12.93,2.81,2.7,21,96,1.54,0.5,0.53,0.75,4.6,0.77,2.31,600,2 12.93,2.81,2.7,21,96,1.54,0.5,0.53,0.75,4.6,0.77,2.31,600,2
13.36,2.56,2.35,20,89,1.4,0.5,0.37,0.64,5.6,0.7,2.47,780,2 13.36,2.56,2.35,20,89,1.4,0.5,0.37,0.64,5.6,0.7,2.47,780,2
13.52,3.17,2.72,23.5,97,1.55,0.52,0.5,0.55,4.35,0.89,2.06,520,2 13.52,3.17,2.72,23.5,97,1.55,0.52,0.5,0.55,4.35,0.89,2.06,520,2
13.62,4.95,2.35,20,92,2,0.8,0.47,1.02,4.4,0.91,2.05,550,2 13.62,4.95,2.35,20,92,2,0.8,0.47,1.02,4.4,0.91,2.05,550,2
12.25,3.88,2.2,18.5,112,1.38,0.78,0.29,1.14,8.21,0.65,2,855,2 12.25,3.88,2.2,18.5,112,1.38,0.78,0.29,1.14,8.21,0.65,2,855,2
13.16,3.57,2.15,21,102,1.5,0.55,0.43,1.3,4,0.6,1.68,830,2 13.16,3.57,2.15,21,102,1.5,0.55,0.43,1.3,4,0.6,1.68,830,2
13.88,5.04,2.23,20,80,0.98,0.34,0.4,0.68,4.9,0.58,1.33,415,2 13.88,5.04,2.23,20,80,0.98,0.34,0.4,0.68,4.9,0.58,1.33,415,2
12.87,4.61,2.48,21.5,86,1.7,0.65,0.47,0.86,7.65,0.54,1.86,625,2 12.87,4.61,2.48,21.5,86,1.7,0.65,0.47,0.86,7.65,0.54,1.86,625,2
13.32,3.24,2.38,21.5,92,1.93,0.76,0.45,1.25,8.42,0.55,1.62,650,2 13.32,3.24,2.38,21.5,92,1.93,0.76,0.45,1.25,8.42,0.55,1.62,650,2
13.08,3.9,2.36,21.5,113,1.41,1.39,0.34,1.14,9.4,0.57,1.33,550,2 13.08,3.9,2.36,21.5,113,1.41,1.39,0.34,1.14,9.4,0.57,1.33,550,2
13.5,3.12,2.62,24,123,1.4,1.57,0.22,1.25,8.6,0.59,1.3,500,2 13.5,3.12,2.62,24,123,1.4,1.57,0.22,1.25,8.6,0.59,1.3,500,2
12.79,2.67,2.48,22,112,1.48,1.36,0.24,1.26,10.8,0.48,1.47,480,2 12.79,2.67,2.48,22,112,1.48,1.36,0.24,1.26,10.8,0.48,1.47,480,2
13.11,1.9,2.75,25.5,116,2.2,1.28,0.26,1.56,7.1,0.61,1.33,425,2 13.11,1.9,2.75,25.5,116,2.2,1.28,0.26,1.56,7.1,0.61,1.33,425,2
13.23,3.3,2.28,18.5,98,1.8,0.83,0.61,1.87,10.52,0.56,1.51,675,2 13.23,3.3,2.28,18.5,98,1.8,0.83,0.61,1.87,10.52,0.56,1.51,675,2
12.58,1.29,2.1,20,103,1.48,0.58,0.53,1.4,7.6,0.58,1.55,640,2 12.58,1.29,2.1,20,103,1.48,0.58,0.53,1.4,7.6,0.58,1.55,640,2
13.17,5.19,2.32,22,93,1.74,0.63,0.61,1.55,7.9,0.6,1.48,725,2 13.17,5.19,2.32,22,93,1.74,0.63,0.61,1.55,7.9,0.6,1.48,725,2
13.84,4.12,2.38,19.5,89,1.8,0.83,0.48,1.56,9.01,0.57,1.64,480,2 13.84,4.12,2.38,19.5,89,1.8,0.83,0.48,1.56,9.01,0.57,1.64,480,2
12.45,3.03,2.64,27,97,1.9,0.58,0.63,1.14,7.5,0.67,1.73,880,2 12.45,3.03,2.64,27,97,1.9,0.58,0.63,1.14,7.5,0.67,1.73,880,2
14.34,1.68,2.7,25,98,2.8,1.31,0.53,2.7,13,0.57,1.96,660,2 14.34,1.68,2.7,25,98,2.8,1.31,0.53,2.7,13,0.57,1.96,660,2
13.48,1.67,2.64,22.5,89,2.6,1.1,0.52,2.29,11.75,0.57,1.78,620,2 13.48,1.67,2.64,22.5,89,2.6,1.1,0.52,2.29,11.75,0.57,1.78,620,2
12.36,3.83,2.38,21,88,2.3,0.92,0.5,1.04,7.65,0.56,1.58,520,2 12.36,3.83,2.38,21,88,2.3,0.92,0.5,1.04,7.65,0.56,1.58,520,2
13.69,3.26,2.54,20,107,1.83,0.56,0.5,0.8,5.88,0.96,1.82,680,2 13.69,3.26,2.54,20,107,1.83,0.56,0.5,0.8,5.88,0.96,1.82,680,2
12.85,3.27,2.58,22,106,1.65,0.6,0.6,0.96,5.58,0.87,2.11,570,2 12.85,3.27,2.58,22,106,1.65,0.6,0.6,0.96,5.58,0.87,2.11,570,2
12.96,3.45,2.35,18.5,106,1.39,0.7,0.4,0.94,5.28,0.68,1.75,675,2 12.96,3.45,2.35,18.5,106,1.39,0.7,0.4,0.94,5.28,0.68,1.75,675,2
13.78,2.76,2.3,22,90,1.35,0.68,0.41,1.03,9.58,0.7,1.68,615,2 13.78,2.76,2.3,22,90,1.35,0.68,0.41,1.03,9.58,0.7,1.68,615,2
13.73,4.36,2.26,22.5,88,1.28,0.47,0.52,1.15,6.62,0.78,1.75,520,2 13.73,4.36,2.26,22.5,88,1.28,0.47,0.52,1.15,6.62,0.78,1.75,520,2
13.45,3.7,2.6,23,111,1.7,0.92,0.43,1.46,10.68,0.85,1.56,695,2 13.45,3.7,2.6,23,111,1.7,0.92,0.43,1.46,10.68,0.85,1.56,695,2
12.82,3.37,2.3,19.5,88,1.48,0.66,0.4,0.97,10.26,0.72,1.75,685,2 12.82,3.37,2.3,19.5,88,1.48,0.66,0.4,0.97,10.26,0.72,1.75,685,2
13.58,2.58,2.69,24.5,105,1.55,0.84,0.39,1.54,8.66,0.74,1.8,750,2 13.58,2.58,2.69,24.5,105,1.55,0.84,0.39,1.54,8.66,0.74,1.8,750,2
13.4,4.6,2.86,25,112,1.98,0.96,0.27,1.11,8.5,0.67,1.92,630,2 13.4,4.6,2.86,25,112,1.98,0.96,0.27,1.11,8.5,0.67,1.92,630,2
12.2,3.03,2.32,19,96,1.25,0.49,0.4,0.73,5.5,0.66,1.83,510,2 12.2,3.03,2.32,19,96,1.25,0.49,0.4,0.73,5.5,0.66,1.83,510,2
12.77,2.39,2.28,19.5,86,1.39,0.51,0.48,0.64,9.899999,0.57,1.63,470,2 12.77,2.39,2.28,19.5,86,1.39,0.51,0.48,0.64,9.899999,0.57,1.63,470,2
14.16,2.51,2.48,20,91,1.68,0.7,0.44,1.24,9.7,0.62,1.71,660,2 14.16,2.51,2.48,20,91,1.68,0.7,0.44,1.24,9.7,0.62,1.71,660,2
13.71,5.65,2.45,20.5,95,1.68,0.61,0.52,1.06,7.7,0.64,1.74,740,2 13.71,5.65,2.45,20.5,95,1.68,0.61,0.52,1.06,7.7,0.64,1.74,740,2
13.4,3.91,2.48,23,102,1.8,0.75,0.43,1.41,7.3,0.7,1.56,750,2 13.4,3.91,2.48,23,102,1.8,0.75,0.43,1.41,7.3,0.7,1.56,750,2
13.27,4.28,2.26,20,120,1.59,0.69,0.43,1.35,10.2,0.59,1.56,835,2 13.27,4.28,2.26,20,120,1.59,0.69,0.43,1.35,10.2,0.59,1.56,835,2
13.17,2.59,2.37,20,120,1.65,0.68,0.53,1.46,9.3,0.6,1.62,840,2 13.17,2.59,2.37,20,120,1.65,0.68,0.53,1.46,9.3,0.6,1.62,840,2
14.13,4.1,2.74,24.5,96,2.05,0.76,0.56,1.35,9.2,0.61,1.6,560,2 14.13,4.1,2.74,24.5,96,2.05,0.76,0.56,1.35,9.2,0.61,1.6,560,2
1 178,13,class_0,class_1,class_2
2 14.23,1.71,2.43,15.6,127,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065,0
3 13.2,1.78,2.14,11.2,100,2.65,2.76,0.26,1.28,4.38,1.05,3.4,1050,0
4 13.16,2.36,2.67,18.6,101,2.8,3.24,0.3,2.81,5.68,1.03,3.17,1185,0
5 14.37,1.95,2.5,16.8,113,3.85,3.49,0.24,2.18,7.8,0.86,3.45,1480,0
6 13.24,2.59,2.87,21,118,2.8,2.69,0.39,1.82,4.32,1.04,2.93,735,0
7 14.2,1.76,2.45,15.2,112,3.27,3.39,0.34,1.97,6.75,1.05,2.85,1450,0
8 14.39,1.87,2.45,14.6,96,2.5,2.52,0.3,1.98,5.25,1.02,3.58,1290,0
9 14.06,2.15,2.61,17.6,121,2.6,2.51,0.31,1.25,5.05,1.06,3.58,1295,0
10 14.83,1.64,2.17,14,97,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045,0
11 13.86,1.35,2.27,16,98,2.98,3.15,0.22,1.85,7.22,1.01,3.55,1045,0
12 14.1,2.16,2.3,18,105,2.95,3.32,0.22,2.38,5.75,1.25,3.17,1510,0
13 14.12,1.48,2.32,16.8,95,2.2,2.43,0.26,1.57,5,1.17,2.82,1280,0
14 13.75,1.73,2.41,16,89,2.6,2.76,0.29,1.81,5.6,1.15,2.9,1320,0
15 14.75,1.73,2.39,11.4,91,3.1,3.69,0.43,2.81,5.4,1.25,2.73,1150,0
16 14.38,1.87,2.38,12,102,3.3,3.64,0.29,2.96,7.5,1.2,3,1547,0
17 13.63,1.81,2.7,17.2,112,2.85,2.91,0.3,1.46,7.3,1.28,2.88,1310,0
18 14.3,1.92,2.72,20,120,2.8,3.14,0.33,1.97,6.2,1.07,2.65,1280,0
19 13.83,1.57,2.62,20,115,2.95,3.4,0.4,1.72,6.6,1.13,2.57,1130,0
20 14.19,1.59,2.48,16.5,108,3.3,3.93,0.32,1.86,8.7,1.23,2.82,1680,0
21 13.64,3.1,2.56,15.2,116,2.7,3.03,0.17,1.66,5.1,0.96,3.36,845,0
22 14.06,1.63,2.28,16,126,3,3.17,0.24,2.1,5.65,1.09,3.71,780,0
23 12.93,3.8,2.65,18.6,102,2.41,2.41,0.25,1.98,4.5,1.03,3.52,770,0
24 13.71,1.86,2.36,16.6,101,2.61,2.88,0.27,1.69,3.8,1.11,4,1035,0
25 12.85,1.6,2.52,17.8,95,2.48,2.37,0.26,1.46,3.93,1.09,3.63,1015,0
26 13.5,1.81,2.61,20,96,2.53,2.61,0.28,1.66,3.52,1.12,3.82,845,0
27 13.05,2.05,3.22,25,124,2.63,2.68,0.47,1.92,3.58,1.13,3.2,830,0
28 13.39,1.77,2.62,16.1,93,2.85,2.94,0.34,1.45,4.8,0.92,3.22,1195,0
29 13.3,1.72,2.14,17,94,2.4,2.19,0.27,1.35,3.95,1.02,2.77,1285,0
30 13.87,1.9,2.8,19.4,107,2.95,2.97,0.37,1.76,4.5,1.25,3.4,915,0
31 14.02,1.68,2.21,16,96,2.65,2.33,0.26,1.98,4.7,1.04,3.59,1035,0
32 13.73,1.5,2.7,22.5,101,3,3.25,0.29,2.38,5.7,1.19,2.71,1285,0
33 13.58,1.66,2.36,19.1,106,2.86,3.19,0.22,1.95,6.9,1.09,2.88,1515,0
34 13.68,1.83,2.36,17.2,104,2.42,2.69,0.42,1.97,3.84,1.23,2.87,990,0
35 13.76,1.53,2.7,19.5,132,2.95,2.74,0.5,1.35,5.4,1.25,3,1235,0
36 13.51,1.8,2.65,19,110,2.35,2.53,0.29,1.54,4.2,1.1,2.87,1095,0
37 13.48,1.81,2.41,20.5,100,2.7,2.98,0.26,1.86,5.1,1.04,3.47,920,0
38 13.28,1.64,2.84,15.5,110,2.6,2.68,0.34,1.36,4.6,1.09,2.78,880,0
39 13.05,1.65,2.55,18,98,2.45,2.43,0.29,1.44,4.25,1.12,2.51,1105,0
40 13.07,1.5,2.1,15.5,98,2.4,2.64,0.28,1.37,3.7,1.18,2.69,1020,0
41 14.22,3.99,2.51,13.2,128,3,3.04,0.2,2.08,5.1,0.89,3.53,760,0
42 13.56,1.71,2.31,16.2,117,3.15,3.29,0.34,2.34,6.13,0.95,3.38,795,0
43 13.41,3.84,2.12,18.8,90,2.45,2.68,0.27,1.48,4.28,0.91,3,1035,0
44 13.88,1.89,2.59,15,101,3.25,3.56,0.17,1.7,5.43,0.88,3.56,1095,0
45 13.24,3.98,2.29,17.5,103,2.64,2.63,0.32,1.66,4.36,0.82,3,680,0
46 13.05,1.77,2.1,17,107,3,3,0.28,2.03,5.04,0.88,3.35,885,0
47 14.21,4.04,2.44,18.9,111,2.85,2.65,0.3,1.25,5.24,0.87,3.33,1080,0
48 14.38,3.59,2.28,16,102,3.25,3.17,0.27,2.19,4.9,1.04,3.44,1065,0
49 13.9,1.68,2.12,16,101,3.1,3.39,0.21,2.14,6.1,0.91,3.33,985,0
50 14.1,2.02,2.4,18.8,103,2.75,2.92,0.32,2.38,6.2,1.07,2.75,1060,0
51 13.94,1.73,2.27,17.4,108,2.88,3.54,0.32,2.08,8.9,1.12,3.1,1260,0
52 13.05,1.73,2.04,12.4,92,2.72,3.27,0.17,2.91,7.2,1.12,2.91,1150,0
53 13.83,1.65,2.6,17.2,94,2.45,2.99,0.22,2.29,5.6,1.24,3.37,1265,0
54 13.82,1.75,2.42,14,111,3.88,3.74,0.32,1.87,7.05,1.01,3.26,1190,0
55 13.77,1.9,2.68,17.1,115,3,2.79,0.39,1.68,6.3,1.13,2.93,1375,0
56 13.74,1.67,2.25,16.4,118,2.6,2.9,0.21,1.62,5.85,0.92,3.2,1060,0
57 13.56,1.73,2.46,20.5,116,2.96,2.78,0.2,2.45,6.25,0.98,3.03,1120,0
58 14.22,1.7,2.3,16.3,118,3.2,3,0.26,2.03,6.38,0.94,3.31,970,0
59 13.29,1.97,2.68,16.8,102,3,3.23,0.31,1.66,6,1.07,2.84,1270,0
60 13.72,1.43,2.5,16.7,108,3.4,3.67,0.19,2.04,6.8,0.89,2.87,1285,0
61 12.37,0.94,1.36,10.6,88,1.98,0.57,0.28,0.42,1.95,1.05,1.82,520,1
62 12.33,1.1,2.28,16,101,2.05,1.09,0.63,0.41,3.27,1.25,1.67,680,1
63 12.64,1.36,2.02,16.8,100,2.02,1.41,0.53,0.62,5.75,0.98,1.59,450,1
64 13.67,1.25,1.92,18,94,2.1,1.79,0.32,0.73,3.8,1.23,2.46,630,1
65 12.37,1.13,2.16,19,87,3.5,3.1,0.19,1.87,4.45,1.22,2.87,420,1
66 12.17,1.45,2.53,19,104,1.89,1.75,0.45,1.03,2.95,1.45,2.23,355,1
67 12.37,1.21,2.56,18.1,98,2.42,2.65,0.37,2.08,4.6,1.19,2.3,678,1
68 13.11,1.01,1.7,15,78,2.98,3.18,0.26,2.28,5.3,1.12,3.18,502,1
69 12.37,1.17,1.92,19.6,78,2.11,2,0.27,1.04,4.68,1.12,3.48,510,1
70 13.34,0.94,2.36,17,110,2.53,1.3,0.55,0.42,3.17,1.02,1.93,750,1
71 12.21,1.19,1.75,16.8,151,1.85,1.28,0.14,2.5,2.85,1.28,3.07,718,1
72 12.29,1.61,2.21,20.4,103,1.1,1.02,0.37,1.46,3.05,0.906,1.82,870,1
73 13.86,1.51,2.67,25,86,2.95,2.86,0.21,1.87,3.38,1.36,3.16,410,1
74 13.49,1.66,2.24,24,87,1.88,1.84,0.27,1.03,3.74,0.98,2.78,472,1
75 12.99,1.67,2.6,30,139,3.3,2.89,0.21,1.96,3.35,1.31,3.5,985,1
76 11.96,1.09,2.3,21,101,3.38,2.14,0.13,1.65,3.21,0.99,3.13,886,1
77 11.66,1.88,1.92,16,97,1.61,1.57,0.34,1.15,3.8,1.23,2.14,428,1
78 13.03,0.9,1.71,16,86,1.95,2.03,0.24,1.46,4.6,1.19,2.48,392,1
79 11.84,2.89,2.23,18,112,1.72,1.32,0.43,0.95,2.65,0.96,2.52,500,1
80 12.33,0.99,1.95,14.8,136,1.9,1.85,0.35,2.76,3.4,1.06,2.31,750,1
81 12.7,3.87,2.4,23,101,2.83,2.55,0.43,1.95,2.57,1.19,3.13,463,1
82 12,0.92,2,19,86,2.42,2.26,0.3,1.43,2.5,1.38,3.12,278,1
83 12.72,1.81,2.2,18.8,86,2.2,2.53,0.26,1.77,3.9,1.16,3.14,714,1
84 12.08,1.13,2.51,24,78,2,1.58,0.4,1.4,2.2,1.31,2.72,630,1
85 13.05,3.86,2.32,22.5,85,1.65,1.59,0.61,1.62,4.8,0.84,2.01,515,1
86 11.84,0.89,2.58,18,94,2.2,2.21,0.22,2.35,3.05,0.79,3.08,520,1
87 12.67,0.98,2.24,18,99,2.2,1.94,0.3,1.46,2.62,1.23,3.16,450,1
88 12.16,1.61,2.31,22.8,90,1.78,1.69,0.43,1.56,2.45,1.33,2.26,495,1
89 11.65,1.67,2.62,26,88,1.92,1.61,0.4,1.34,2.6,1.36,3.21,562,1
90 11.64,2.06,2.46,21.6,84,1.95,1.69,0.48,1.35,2.8,1,2.75,680,1
91 12.08,1.33,2.3,23.6,70,2.2,1.59,0.42,1.38,1.74,1.07,3.21,625,1
92 12.08,1.83,2.32,18.5,81,1.6,1.5,0.52,1.64,2.4,1.08,2.27,480,1
93 12,1.51,2.42,22,86,1.45,1.25,0.5,1.63,3.6,1.05,2.65,450,1
94 12.69,1.53,2.26,20.7,80,1.38,1.46,0.58,1.62,3.05,0.96,2.06,495,1
95 12.29,2.83,2.22,18,88,2.45,2.25,0.25,1.99,2.15,1.15,3.3,290,1
96 11.62,1.99,2.28,18,98,3.02,2.26,0.17,1.35,3.25,1.16,2.96,345,1
97 12.47,1.52,2.2,19,162,2.5,2.27,0.32,3.28,2.6,1.16,2.63,937,1
98 11.81,2.12,2.74,21.5,134,1.6,0.99,0.14,1.56,2.5,0.95,2.26,625,1
99 12.29,1.41,1.98,16,85,2.55,2.5,0.29,1.77,2.9,1.23,2.74,428,1
100 12.37,1.07,2.1,18.5,88,3.52,3.75,0.24,1.95,4.5,1.04,2.77,660,1
101 12.29,3.17,2.21,18,88,2.85,2.99,0.45,2.81,2.3,1.42,2.83,406,1
102 12.08,2.08,1.7,17.5,97,2.23,2.17,0.26,1.4,3.3,1.27,2.96,710,1
103 12.6,1.34,1.9,18.5,88,1.45,1.36,0.29,1.35,2.45,1.04,2.77,562,1
104 12.34,2.45,2.46,21,98,2.56,2.11,0.34,1.31,2.8,0.8,3.38,438,1
105 11.82,1.72,1.88,19.5,86,2.5,1.64,0.37,1.42,2.06,0.94,2.44,415,1
106 12.51,1.73,1.98,20.5,85,2.2,1.92,0.32,1.48,2.94,1.04,3.57,672,1
107 12.42,2.55,2.27,22,90,1.68,1.84,0.66,1.42,2.7,0.86,3.3,315,1
108 12.25,1.73,2.12,19,80,1.65,2.03,0.37,1.63,3.4,1,3.17,510,1
109 12.72,1.75,2.28,22.5,84,1.38,1.76,0.48,1.63,3.3,0.88,2.42,488,1
110 12.22,1.29,1.94,19,92,2.36,2.04,0.39,2.08,2.7,0.86,3.02,312,1
111 11.61,1.35,2.7,20,94,2.74,2.92,0.29,2.49,2.65,0.96,3.26,680,1
112 11.46,3.74,1.82,19.5,107,3.18,2.58,0.24,3.58,2.9,0.75,2.81,562,1
113 12.52,2.43,2.17,21,88,2.55,2.27,0.26,1.22,2,0.9,2.78,325,1
114 11.76,2.68,2.92,20,103,1.75,2.03,0.6,1.05,3.8,1.23,2.5,607,1
115 11.41,0.74,2.5,21,88,2.48,2.01,0.42,1.44,3.08,1.1,2.31,434,1
116 12.08,1.39,2.5,22.5,84,2.56,2.29,0.43,1.04,2.9,0.93,3.19,385,1
117 11.03,1.51,2.2,21.5,85,2.46,2.17,0.52,2.01,1.9,1.71,2.87,407,1
118 11.82,1.47,1.99,20.8,86,1.98,1.6,0.3,1.53,1.95,0.95,3.33,495,1
119 12.42,1.61,2.19,22.5,108,2,2.09,0.34,1.61,2.06,1.06,2.96,345,1
120 12.77,3.43,1.98,16,80,1.63,1.25,0.43,0.83,3.4,0.7,2.12,372,1
121 12,3.43,2,19,87,2,1.64,0.37,1.87,1.28,0.93,3.05,564,1
122 11.45,2.4,2.42,20,96,2.9,2.79,0.32,1.83,3.25,0.8,3.39,625,1
123 11.56,2.05,3.23,28.5,119,3.18,5.08,0.47,1.87,6,0.93,3.69,465,1
124 12.42,4.43,2.73,26.5,102,2.2,2.13,0.43,1.71,2.08,0.92,3.12,365,1
125 13.05,5.8,2.13,21.5,86,2.62,2.65,0.3,2.01,2.6,0.73,3.1,380,1
126 11.87,4.31,2.39,21,82,2.86,3.03,0.21,2.91,2.8,0.75,3.64,380,1
127 12.07,2.16,2.17,21,85,2.6,2.65,0.37,1.35,2.76,0.86,3.28,378,1
128 12.43,1.53,2.29,21.5,86,2.74,3.15,0.39,1.77,3.94,0.69,2.84,352,1
129 11.79,2.13,2.78,28.5,92,2.13,2.24,0.58,1.76,3,0.97,2.44,466,1
130 12.37,1.63,2.3,24.5,88,2.22,2.45,0.4,1.9,2.12,0.89,2.78,342,1
131 12.04,4.3,2.38,22,80,2.1,1.75,0.42,1.35,2.6,0.79,2.57,580,1
132 12.86,1.35,2.32,18,122,1.51,1.25,0.21,0.94,4.1,0.76,1.29,630,2
133 12.88,2.99,2.4,20,104,1.3,1.22,0.24,0.83,5.4,0.74,1.42,530,2
134 12.81,2.31,2.4,24,98,1.15,1.09,0.27,0.83,5.7,0.66,1.36,560,2
135 12.7,3.55,2.36,21.5,106,1.7,1.2,0.17,0.84,5,0.78,1.29,600,2
136 12.51,1.24,2.25,17.5,85,2,0.58,0.6,1.25,5.45,0.75,1.51,650,2
137 12.6,2.46,2.2,18.5,94,1.62,0.66,0.63,0.94,7.1,0.73,1.58,695,2
138 12.25,4.72,2.54,21,89,1.38,0.47,0.53,0.8,3.85,0.75,1.27,720,2
139 12.53,5.51,2.64,25,96,1.79,0.6,0.63,1.1,5,0.82,1.69,515,2
140 13.49,3.59,2.19,19.5,88,1.62,0.48,0.58,0.88,5.7,0.81,1.82,580,2
141 12.84,2.96,2.61,24,101,2.32,0.6,0.53,0.81,4.92,0.89,2.15,590,2
142 12.93,2.81,2.7,21,96,1.54,0.5,0.53,0.75,4.6,0.77,2.31,600,2
143 13.36,2.56,2.35,20,89,1.4,0.5,0.37,0.64,5.6,0.7,2.47,780,2
144 13.52,3.17,2.72,23.5,97,1.55,0.52,0.5,0.55,4.35,0.89,2.06,520,2
145 13.62,4.95,2.35,20,92,2,0.8,0.47,1.02,4.4,0.91,2.05,550,2
146 12.25,3.88,2.2,18.5,112,1.38,0.78,0.29,1.14,8.21,0.65,2,855,2
147 13.16,3.57,2.15,21,102,1.5,0.55,0.43,1.3,4,0.6,1.68,830,2
148 13.88,5.04,2.23,20,80,0.98,0.34,0.4,0.68,4.9,0.58,1.33,415,2
149 12.87,4.61,2.48,21.5,86,1.7,0.65,0.47,0.86,7.65,0.54,1.86,625,2
150 13.32,3.24,2.38,21.5,92,1.93,0.76,0.45,1.25,8.42,0.55,1.62,650,2
151 13.08,3.9,2.36,21.5,113,1.41,1.39,0.34,1.14,9.4,0.57,1.33,550,2
152 13.5,3.12,2.62,24,123,1.4,1.57,0.22,1.25,8.6,0.59,1.3,500,2
153 12.79,2.67,2.48,22,112,1.48,1.36,0.24,1.26,10.8,0.48,1.47,480,2
154 13.11,1.9,2.75,25.5,116,2.2,1.28,0.26,1.56,7.1,0.61,1.33,425,2
155 13.23,3.3,2.28,18.5,98,1.8,0.83,0.61,1.87,10.52,0.56,1.51,675,2
156 12.58,1.29,2.1,20,103,1.48,0.58,0.53,1.4,7.6,0.58,1.55,640,2
157 13.17,5.19,2.32,22,93,1.74,0.63,0.61,1.55,7.9,0.6,1.48,725,2
158 13.84,4.12,2.38,19.5,89,1.8,0.83,0.48,1.56,9.01,0.57,1.64,480,2
159 12.45,3.03,2.64,27,97,1.9,0.58,0.63,1.14,7.5,0.67,1.73,880,2
160 14.34,1.68,2.7,25,98,2.8,1.31,0.53,2.7,13,0.57,1.96,660,2
161 13.48,1.67,2.64,22.5,89,2.6,1.1,0.52,2.29,11.75,0.57,1.78,620,2
162 12.36,3.83,2.38,21,88,2.3,0.92,0.5,1.04,7.65,0.56,1.58,520,2
163 13.69,3.26,2.54,20,107,1.83,0.56,0.5,0.8,5.88,0.96,1.82,680,2
164 12.85,3.27,2.58,22,106,1.65,0.6,0.6,0.96,5.58,0.87,2.11,570,2
165 12.96,3.45,2.35,18.5,106,1.39,0.7,0.4,0.94,5.28,0.68,1.75,675,2
166 13.78,2.76,2.3,22,90,1.35,0.68,0.41,1.03,9.58,0.7,1.68,615,2
167 13.73,4.36,2.26,22.5,88,1.28,0.47,0.52,1.15,6.62,0.78,1.75,520,2
168 13.45,3.7,2.6,23,111,1.7,0.92,0.43,1.46,10.68,0.85,1.56,695,2
169 12.82,3.37,2.3,19.5,88,1.48,0.66,0.4,0.97,10.26,0.72,1.75,685,2
170 13.58,2.58,2.69,24.5,105,1.55,0.84,0.39,1.54,8.66,0.74,1.8,750,2
171 13.4,4.6,2.86,25,112,1.98,0.96,0.27,1.11,8.5,0.67,1.92,630,2
172 12.2,3.03,2.32,19,96,1.25,0.49,0.4,0.73,5.5,0.66,1.83,510,2
173 12.77,2.39,2.28,19.5,86,1.39,0.51,0.48,0.64,9.899999,0.57,1.63,470,2
174 14.16,2.51,2.48,20,91,1.68,0.7,0.44,1.24,9.7,0.62,1.71,660,2
175 13.71,5.65,2.45,20.5,95,1.68,0.61,0.52,1.06,7.7,0.64,1.74,740,2
176 13.4,3.91,2.48,23,102,1.8,0.75,0.43,1.41,7.3,0.7,1.56,750,2
177 13.27,4.28,2.26,20,120,1.59,0.69,0.43,1.35,10.2,0.59,1.56,835,2
178 13.17,2.59,2.37,20,120,1.65,0.68,0.53,1.46,9.3,0.6,1.62,840,2
179 14.13,4.1,2.74,24.5,96,2.05,0.76,0.56,1.35,9.2,0.61,1.6,560,2
@@ -1,118 +1,118 @@
.. _breast_cancer_dataset: .. _breast_cancer_dataset:
Breast cancer Wisconsin (diagnostic) dataset Breast cancer Wisconsin (diagnostic) dataset
-------------------------------------------- --------------------------------------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 569 :Number of Instances: 569
:Number of Attributes: 30 numeric, predictive attributes and the class :Number of Attributes: 30 numeric, predictive attributes and the class
:Attribute Information: :Attribute Information:
- radius (mean of distances from center to points on the perimeter) - radius (mean of distances from center to points on the perimeter)
- texture (standard deviation of gray-scale values) - texture (standard deviation of gray-scale values)
- perimeter - perimeter
- area - area
- smoothness (local variation in radius lengths) - smoothness (local variation in radius lengths)
- compactness (perimeter^2 / area - 1.0) - compactness (perimeter^2 / area - 1.0)
- concavity (severity of concave portions of the contour) - concavity (severity of concave portions of the contour)
- concave points (number of concave portions of the contour) - concave points (number of concave portions of the contour)
- symmetry - symmetry
- fractal dimension ("coastline approximation" - 1) - fractal dimension ("coastline approximation" - 1)
The mean, standard error, and "worst" or largest (mean of the three The mean, standard error, and "worst" or largest (mean of the three
worst/largest values) of these features were computed for each image, worst/largest values) of these features were computed for each image,
resulting in 30 features. For instance, field 0 is Mean Radius, field resulting in 30 features. For instance, field 0 is Mean Radius, field
10 is Radius SE, field 20 is Worst Radius. 10 is Radius SE, field 20 is Worst Radius.
- class: - class:
- WDBC-Malignant - WDBC-Malignant
- WDBC-Benign - WDBC-Benign
:Summary Statistics: :Summary Statistics:
===================================== ====== ====== ===================================== ====== ======
Min Max Min Max
===================================== ====== ====== ===================================== ====== ======
radius (mean): 6.981 28.11 radius (mean): 6.981 28.11
texture (mean): 9.71 39.28 texture (mean): 9.71 39.28
perimeter (mean): 43.79 188.5 perimeter (mean): 43.79 188.5
area (mean): 143.5 2501.0 area (mean): 143.5 2501.0
smoothness (mean): 0.053 0.163 smoothness (mean): 0.053 0.163
compactness (mean): 0.019 0.345 compactness (mean): 0.019 0.345
concavity (mean): 0.0 0.427 concavity (mean): 0.0 0.427
concave points (mean): 0.0 0.201 concave points (mean): 0.0 0.201
symmetry (mean): 0.106 0.304 symmetry (mean): 0.106 0.304
fractal dimension (mean): 0.05 0.097 fractal dimension (mean): 0.05 0.097
radius (standard error): 0.112 2.873 radius (standard error): 0.112 2.873
texture (standard error): 0.36 4.885 texture (standard error): 0.36 4.885
perimeter (standard error): 0.757 21.98 perimeter (standard error): 0.757 21.98
area (standard error): 6.802 542.2 area (standard error): 6.802 542.2
smoothness (standard error): 0.002 0.031 smoothness (standard error): 0.002 0.031
compactness (standard error): 0.002 0.135 compactness (standard error): 0.002 0.135
concavity (standard error): 0.0 0.396 concavity (standard error): 0.0 0.396
concave points (standard error): 0.0 0.053 concave points (standard error): 0.0 0.053
symmetry (standard error): 0.008 0.079 symmetry (standard error): 0.008 0.079
fractal dimension (standard error): 0.001 0.03 fractal dimension (standard error): 0.001 0.03
radius (worst): 7.93 36.04 radius (worst): 7.93 36.04
texture (worst): 12.02 49.54 texture (worst): 12.02 49.54
perimeter (worst): 50.41 251.2 perimeter (worst): 50.41 251.2
area (worst): 185.2 4254.0 area (worst): 185.2 4254.0
smoothness (worst): 0.071 0.223 smoothness (worst): 0.071 0.223
compactness (worst): 0.027 1.058 compactness (worst): 0.027 1.058
concavity (worst): 0.0 1.252 concavity (worst): 0.0 1.252
concave points (worst): 0.0 0.291 concave points (worst): 0.0 0.291
symmetry (worst): 0.156 0.664 symmetry (worst): 0.156 0.664
fractal dimension (worst): 0.055 0.208 fractal dimension (worst): 0.055 0.208
===================================== ====== ====== ===================================== ====== ======
:Missing Attribute Values: None :Missing Attribute Values: None
:Class Distribution: 212 - Malignant, 357 - Benign :Class Distribution: 212 - Malignant, 357 - Benign
:Creator: Dr. William H. Wolberg, W. Nick Street, Olvi L. Mangasarian :Creator: Dr. William H. Wolberg, W. Nick Street, Olvi L. Mangasarian
:Donor: Nick Street :Donor: Nick Street
:Date: November, 1995 :Date: November, 1995
This is a copy of UCI ML Breast Cancer Wisconsin (Diagnostic) datasets. This is a copy of UCI ML Breast Cancer Wisconsin (Diagnostic) datasets.
https://goo.gl/U2Uwz2 https://goo.gl/U2Uwz2
Features are computed from a digitized image of a fine needle Features are computed from a digitized image of a fine needle
aspirate (FNA) of a breast mass. They describe aspirate (FNA) of a breast mass. They describe
characteristics of the cell nuclei present in the image. characteristics of the cell nuclei present in the image.
Separating plane described above was obtained using Separating plane described above was obtained using
Multisurface Method-Tree (MSM-T) [K. P. Bennett, "Decision Tree Multisurface Method-Tree (MSM-T) [K. P. Bennett, "Decision Tree
Construction Via Linear Programming." Proceedings of the 4th Construction Via Linear Programming." Proceedings of the 4th
Midwest Artificial Intelligence and Cognitive Science Society, Midwest Artificial Intelligence and Cognitive Science Society,
pp. 97-101, 1992], a classification method which uses linear pp. 97-101, 1992], a classification method which uses linear
programming to construct a decision tree. Relevant features programming to construct a decision tree. Relevant features
were selected using an exhaustive search in the space of 1-4 were selected using an exhaustive search in the space of 1-4
features and 1-3 separating planes. features and 1-3 separating planes.
The actual linear program used to obtain the separating plane The actual linear program used to obtain the separating plane
in the 3-dimensional space is that described in: in the 3-dimensional space is that described in:
[K. P. Bennett and O. L. Mangasarian: "Robust Linear [K. P. Bennett and O. L. Mangasarian: "Robust Linear
Programming Discrimination of Two Linearly Inseparable Sets", Programming Discrimination of Two Linearly Inseparable Sets",
Optimization Methods and Software 1, 1992, 23-34]. Optimization Methods and Software 1, 1992, 23-34].
This database is also available through the UW CS ftp server: This database is also available through the UW CS ftp server:
ftp ftp.cs.wisc.edu ftp ftp.cs.wisc.edu
cd math-prog/cpo-dataset/machine-learn/WDBC/ cd math-prog/cpo-dataset/machine-learn/WDBC/
.. dropdown:: References .. dropdown:: References
- W.N. Street, W.H. Wolberg and O.L. Mangasarian. Nuclear feature extraction - W.N. Street, W.H. Wolberg and O.L. Mangasarian. Nuclear feature extraction
for breast tumor diagnosis. IS&T/SPIE 1993 International Symposium on for breast tumor diagnosis. IS&T/SPIE 1993 International Symposium on
Electronic Imaging: Science and Technology, volume 1905, pages 861-870, Electronic Imaging: Science and Technology, volume 1905, pages 861-870,
San Jose, CA, 1993. San Jose, CA, 1993.
- O.L. Mangasarian, W.N. Street and W.H. Wolberg. Breast cancer diagnosis and - O.L. Mangasarian, W.N. Street and W.H. Wolberg. Breast cancer diagnosis and
prognosis via linear programming. Operations Research, 43(4), pages 570-577, prognosis via linear programming. Operations Research, 43(4), pages 570-577,
July-August 1995. July-August 1995.
- W.H. Wolberg, W.N. Street, and O.L. Mangasarian. Machine learning techniques - W.H. Wolberg, W.N. Street, and O.L. Mangasarian. Machine learning techniques
to diagnose breast cancer from fine-needle aspirates. Cancer Letters 77 (1994) to diagnose breast cancer from fine-needle aspirates. Cancer Letters 77 (1994)
163-171. 163-171.
@@ -1,46 +1,46 @@
.. _california_housing_dataset: .. _california_housing_dataset:
California Housing dataset California Housing dataset
-------------------------- --------------------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 20640 :Number of Instances: 20640
:Number of Attributes: 8 numeric, predictive attributes and the target :Number of Attributes: 8 numeric, predictive attributes and the target
:Attribute Information: :Attribute Information:
- MedInc median income in block group - MedInc median income in block group
- HouseAge median house age in block group - HouseAge median house age in block group
- AveRooms average number of rooms per household - AveRooms average number of rooms per household
- AveBedrms average number of bedrooms per household - AveBedrms average number of bedrooms per household
- Population block group population - Population block group population
- AveOccup average number of household members - AveOccup average number of household members
- Latitude block group latitude - Latitude block group latitude
- Longitude block group longitude - Longitude block group longitude
:Missing Attribute Values: None :Missing Attribute Values: None
This dataset was obtained from the StatLib repository. This dataset was obtained from the StatLib repository.
https://www.dcc.fc.up.pt/~ltorgo/Regression/cal_housing.html https://www.dcc.fc.up.pt/~ltorgo/Regression/cal_housing.html
The target variable is the median house value for California districts, The target variable is the median house value for California districts,
expressed in hundreds of thousands of dollars ($100,000). expressed in hundreds of thousands of dollars ($100,000).
This dataset was derived from the 1990 U.S. census, using one row per census This dataset was derived from the 1990 U.S. census, using one row per census
block group. A block group is the smallest geographical unit for which the U.S. block group. A block group is the smallest geographical unit for which the U.S.
Census Bureau publishes sample data (a block group typically has a population Census Bureau publishes sample data (a block group typically has a population
of 600 to 3,000 people). of 600 to 3,000 people).
A household is a group of people residing within a home. Since the average A household is a group of people residing within a home. Since the average
number of rooms and bedrooms in this dataset are provided per household, these number of rooms and bedrooms in this dataset are provided per household, these
columns may take surprisingly large values for block groups with few households columns may take surprisingly large values for block groups with few households
and many empty houses, such as vacation resorts. and many empty houses, such as vacation resorts.
It can be downloaded/loaded using the It can be downloaded/loaded using the
:func:`sklearn.datasets.fetch_california_housing` function. :func:`sklearn.datasets.fetch_california_housing` function.
.. rubric:: References .. rubric:: References
- Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions, - Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,
Statistics and Probability Letters, 33:291-297, 1997. Statistics and Probability Letters, 33:291-297, 1997.
@@ -1,30 +1,30 @@
.. _covtype_dataset: .. _covtype_dataset:
Forest covertypes Forest covertypes
----------------- -----------------
The samples in this dataset correspond to 30×30m patches of forest in the US, The samples in this dataset correspond to 30×30m patches of forest in the US,
collected for the task of predicting each patch's cover type, collected for the task of predicting each patch's cover type,
i.e. the dominant species of tree. i.e. the dominant species of tree.
There are seven covertypes, making this a multiclass classification problem. There are seven covertypes, making this a multiclass classification problem.
Each sample has 54 features, described on the Each sample has 54 features, described on the
`dataset's homepage <https://archive.ics.uci.edu/ml/datasets/Covertype>`__. `dataset's homepage <https://archive.ics.uci.edu/ml/datasets/Covertype>`__.
Some of the features are boolean indicators, Some of the features are boolean indicators,
while others are discrete or continuous measurements. while others are discrete or continuous measurements.
**Data Set Characteristics:** **Data Set Characteristics:**
================= ============ ================= ============
Classes 7 Classes 7
Samples total 581012 Samples total 581012
Dimensionality 54 Dimensionality 54
Features int Features int
================= ============ ================= ============
:func:`sklearn.datasets.fetch_covtype` will load the covertype dataset; :func:`sklearn.datasets.fetch_covtype` will load the covertype dataset;
it returns a dictionary-like 'Bunch' object it returns a dictionary-like 'Bunch' object
with the feature matrix in the ``data`` member with the feature matrix in the ``data`` member
and the target values in ``target``. If optional argument 'as_frame' is and the target values in ``target``. If optional argument 'as_frame' is
set to 'True', it will return ``data`` and ``target`` as pandas set to 'True', it will return ``data`` and ``target`` as pandas
data frame, and there will be an additional member ``frame`` as well. data frame, and there will be an additional member ``frame`` as well.
The dataset will be downloaded from the web if necessary. The dataset will be downloaded from the web if necessary.
@@ -1,38 +1,38 @@
.. _diabetes_dataset: .. _diabetes_dataset:
Diabetes dataset Diabetes dataset
---------------- ----------------
Ten baseline variables, age, sex, body mass index, average blood Ten baseline variables, age, sex, body mass index, average blood
pressure, and six blood serum measurements were obtained for each of n = pressure, and six blood serum measurements were obtained for each of n =
442 diabetes patients, as well as the response of interest, a 442 diabetes patients, as well as the response of interest, a
quantitative measure of disease progression one year after baseline. quantitative measure of disease progression one year after baseline.
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 442 :Number of Instances: 442
:Number of Attributes: First 10 columns are numeric predictive values :Number of Attributes: First 10 columns are numeric predictive values
:Target: Column 11 is a quantitative measure of disease progression one year after baseline :Target: Column 11 is a quantitative measure of disease progression one year after baseline
:Attribute Information: :Attribute Information:
- age age in years - age age in years
- sex - sex
- bmi body mass index - bmi body mass index
- bp average blood pressure - bp average blood pressure
- s1 tc, total serum cholesterol - s1 tc, total serum cholesterol
- s2 ldl, low-density lipoproteins - s2 ldl, low-density lipoproteins
- s3 hdl, high-density lipoproteins - s3 hdl, high-density lipoproteins
- s4 tch, total cholesterol / HDL - s4 tch, total cholesterol / HDL
- s5 ltg, possibly log of serum triglycerides level - s5 ltg, possibly log of serum triglycerides level
- s6 glu, blood sugar level - s6 glu, blood sugar level
Note: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times the square root of `n_samples` (i.e. the sum of squares of each column totals 1). Note: Each of these 10 feature variables have been mean centered and scaled by the standard deviation times the square root of `n_samples` (i.e. the sum of squares of each column totals 1).
Source URL: Source URL:
https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html
For more information see: For more information see:
Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499. Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) "Least Angle Regression," Annals of Statistics (with discussion), 407-499.
(https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf) (https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf)
@@ -1,46 +1,46 @@
.. _digits_dataset: .. _digits_dataset:
Optical recognition of handwritten digits dataset Optical recognition of handwritten digits dataset
-------------------------------------------------- --------------------------------------------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 1797 :Number of Instances: 1797
:Number of Attributes: 64 :Number of Attributes: 64
:Attribute Information: 8x8 image of integer pixels in the range 0..16. :Attribute Information: 8x8 image of integer pixels in the range 0..16.
:Missing Attribute Values: None :Missing Attribute Values: None
:Creator: E. Alpaydin (alpaydin '@' boun.edu.tr) :Creator: E. Alpaydin (alpaydin '@' boun.edu.tr)
:Date: July; 1998 :Date: July; 1998
This is a copy of the test set of the UCI ML hand-written digits datasets This is a copy of the test set of the UCI ML hand-written digits datasets
https://archive.ics.uci.edu/ml/datasets/Optical+Recognition+of+Handwritten+Digits https://archive.ics.uci.edu/ml/datasets/Optical+Recognition+of+Handwritten+Digits
The data set contains images of hand-written digits: 10 classes where The data set contains images of hand-written digits: 10 classes where
each class refers to a digit. each class refers to a digit.
Preprocessing programs made available by NIST were used to extract Preprocessing programs made available by NIST were used to extract
normalized bitmaps of handwritten digits from a preprinted form. From a normalized bitmaps of handwritten digits from a preprinted form. From a
total of 43 people, 30 contributed to the training set and different 13 total of 43 people, 30 contributed to the training set and different 13
to the test set. 32x32 bitmaps are divided into nonoverlapping blocks of to the test set. 32x32 bitmaps are divided into nonoverlapping blocks of
4x4 and the number of on pixels are counted in each block. This generates 4x4 and the number of on pixels are counted in each block. This generates
an input matrix of 8x8 where each element is an integer in the range an input matrix of 8x8 where each element is an integer in the range
0..16. This reduces dimensionality and gives invariance to small 0..16. This reduces dimensionality and gives invariance to small
distortions. distortions.
For info on NIST preprocessing routines, see M. D. Garris, J. L. Blue, G. For info on NIST preprocessing routines, see M. D. Garris, J. L. Blue, G.
T. Candela, D. L. Dimmick, J. Geist, P. J. Grother, S. A. Janet, and C. T. Candela, D. L. Dimmick, J. Geist, P. J. Grother, S. A. Janet, and C.
L. Wilson, NIST Form-Based Handprint Recognition System, NISTIR 5469, L. Wilson, NIST Form-Based Handprint Recognition System, NISTIR 5469,
1994. 1994.
.. dropdown:: References .. dropdown:: References
- C. Kaynak (1995) Methods of Combining Multiple Classifiers and Their - C. Kaynak (1995) Methods of Combining Multiple Classifiers and Their
Applications to Handwritten Digit Recognition, MSc Thesis, Institute of Applications to Handwritten Digit Recognition, MSc Thesis, Institute of
Graduate Studies in Science and Engineering, Bogazici University. Graduate Studies in Science and Engineering, Bogazici University.
- E. Alpaydin, C. Kaynak (1998) Cascading Classifiers, Kybernetika. - E. Alpaydin, C. Kaynak (1998) Cascading Classifiers, Kybernetika.
- Ken Tang and Ponnuthurai N. Suganthan and Xi Yao and A. Kai Qin. - Ken Tang and Ponnuthurai N. Suganthan and Xi Yao and A. Kai Qin.
Linear dimensionalityreduction using relevance weighted LDA. School of Linear dimensionalityreduction using relevance weighted LDA. School of
Electrical and Electronic Engineering Nanyang Technological University. Electrical and Electronic Engineering Nanyang Technological University.
2005. 2005.
- Claudio Gentile. A New Approximate Maximal Margin Classification - Claudio Gentile. A New Approximate Maximal Margin Classification
Algorithm. NIPS. 2000. Algorithm. NIPS. 2000.
@@ -1,63 +1,63 @@
.. _iris_dataset: .. _iris_dataset:
Iris plants dataset Iris plants dataset
-------------------- --------------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 150 (50 in each of three classes) :Number of Instances: 150 (50 in each of three classes)
:Number of Attributes: 4 numeric, predictive attributes and the class :Number of Attributes: 4 numeric, predictive attributes and the class
:Attribute Information: :Attribute Information:
- sepal length in cm - sepal length in cm
- sepal width in cm - sepal width in cm
- petal length in cm - petal length in cm
- petal width in cm - petal width in cm
- class: - class:
- Iris-Setosa - Iris-Setosa
- Iris-Versicolour - Iris-Versicolour
- Iris-Virginica - Iris-Virginica
:Summary Statistics: :Summary Statistics:
============== ==== ==== ======= ===== ==================== ============== ==== ==== ======= ===== ====================
Min Max Mean SD Class Correlation Min Max Mean SD Class Correlation
============== ==== ==== ======= ===== ==================== ============== ==== ==== ======= ===== ====================
sepal length: 4.3 7.9 5.84 0.83 0.7826 sepal length: 4.3 7.9 5.84 0.83 0.7826
sepal width: 2.0 4.4 3.05 0.43 -0.4194 sepal width: 2.0 4.4 3.05 0.43 -0.4194
petal length: 1.0 6.9 3.76 1.76 0.9490 (high!) petal length: 1.0 6.9 3.76 1.76 0.9490 (high!)
petal width: 0.1 2.5 1.20 0.76 0.9565 (high!) petal width: 0.1 2.5 1.20 0.76 0.9565 (high!)
============== ==== ==== ======= ===== ==================== ============== ==== ==== ======= ===== ====================
:Missing Attribute Values: None :Missing Attribute Values: None
:Class Distribution: 33.3% for each of 3 classes. :Class Distribution: 33.3% for each of 3 classes.
:Creator: R.A. Fisher :Creator: R.A. Fisher
:Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov) :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
:Date: July, 1988 :Date: July, 1988
The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken
from Fisher's paper. Note that it's the same as in R, but not as in the UCI from Fisher's paper. Note that it's the same as in R, but not as in the UCI
Machine Learning Repository, which has two wrong data points. Machine Learning Repository, which has two wrong data points.
This is perhaps the best known database to be found in the This is perhaps the best known database to be found in the
pattern recognition literature. Fisher's paper is a classic in the field and pattern recognition literature. Fisher's paper is a classic in the field and
is referenced frequently to this day. (See Duda & Hart, for example.) The is referenced frequently to this day. (See Duda & Hart, for example.) The
data set contains 3 classes of 50 instances each, where each class refers to a data set contains 3 classes of 50 instances each, where each class refers to a
type of iris plant. One class is linearly separable from the other 2; the type of iris plant. One class is linearly separable from the other 2; the
latter are NOT linearly separable from each other. latter are NOT linearly separable from each other.
.. dropdown:: References .. dropdown:: References
- Fisher, R.A. "The use of multiple measurements in taxonomic problems" - Fisher, R.A. "The use of multiple measurements in taxonomic problems"
Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to
Mathematical Statistics" (John Wiley, NY, 1950). Mathematical Statistics" (John Wiley, NY, 1950).
- Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis. - Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis.
(Q327.D83) John Wiley & Sons. ISBN 0-471-22361-1. See page 218. (Q327.D83) John Wiley & Sons. ISBN 0-471-22361-1. See page 218.
- Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System - Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System
Structure and Classification Rule for Recognition in Partially Exposed Structure and Classification Rule for Recognition in Partially Exposed
Environments". IEEE Transactions on Pattern Analysis and Machine Environments". IEEE Transactions on Pattern Analysis and Machine
Intelligence, Vol. PAMI-2, No. 1, 67-71. Intelligence, Vol. PAMI-2, No. 1, 67-71.
- Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule". IEEE Transactions - Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule". IEEE Transactions
on Information Theory, May 1972, 431-433. on Information Theory, May 1972, 431-433.
- See also: 1988 MLC Proceedings, 54-64. Cheeseman et al"s AUTOCLASS II - See also: 1988 MLC Proceedings, 54-64. Cheeseman et al"s AUTOCLASS II
conceptual clustering system finds 3 classes in the data. conceptual clustering system finds 3 classes in the data.
- Many, many more ... - Many, many more ...
@@ -1,94 +1,94 @@
.. _kddcup99_dataset: .. _kddcup99_dataset:
Kddcup 99 dataset Kddcup 99 dataset
----------------- -----------------
The KDD Cup '99 dataset was created by processing the tcpdump portions The KDD Cup '99 dataset was created by processing the tcpdump portions
of the 1998 DARPA Intrusion Detection System (IDS) Evaluation dataset, of the 1998 DARPA Intrusion Detection System (IDS) Evaluation dataset,
created by MIT Lincoln Lab [2]_. The artificial data (described on the `dataset's created by MIT Lincoln Lab [2]_. The artificial data (described on the `dataset's
homepage <https://kdd.ics.uci.edu/databases/kddcup99/kddcup99.html>`_) was homepage <https://kdd.ics.uci.edu/databases/kddcup99/kddcup99.html>`_) was
generated using a closed network and hand-injected attacks to produce a generated using a closed network and hand-injected attacks to produce a
large number of different types of attack with normal activity in the large number of different types of attack with normal activity in the
background. As the initial goal was to produce a large training set for background. As the initial goal was to produce a large training set for
supervised learning algorithms, there is a large proportion (80.1%) of supervised learning algorithms, there is a large proportion (80.1%) of
abnormal data which is unrealistic in real world, and inappropriate for abnormal data which is unrealistic in real world, and inappropriate for
unsupervised anomaly detection which aims at detecting 'abnormal' data, i.e.: unsupervised anomaly detection which aims at detecting 'abnormal' data, i.e.:
* qualitatively different from normal data * qualitatively different from normal data
* in large minority among the observations. * in large minority among the observations.
We thus transform the KDD Data set into two different data sets: SA and SF. We thus transform the KDD Data set into two different data sets: SA and SF.
* SA is obtained by simply selecting all the normal data, and a small * SA is obtained by simply selecting all the normal data, and a small
proportion of abnormal data to gives an anomaly proportion of 1%. proportion of abnormal data to gives an anomaly proportion of 1%.
* SF is obtained as in [3]_ * SF is obtained as in [3]_
by simply picking up the data whose attribute logged_in is positive, thus by simply picking up the data whose attribute logged_in is positive, thus
focusing on the intrusion attack, which gives a proportion of 0.3% of focusing on the intrusion attack, which gives a proportion of 0.3% of
attack. attack.
* http and smtp are two subsets of SF corresponding with third feature * http and smtp are two subsets of SF corresponding with third feature
equal to 'http' (resp. to 'smtp'). equal to 'http' (resp. to 'smtp').
General KDD structure: General KDD structure:
================ ========================================== ================ ==========================================
Samples total 4898431 Samples total 4898431
Dimensionality 41 Dimensionality 41
Features discrete (int) or continuous (float) Features discrete (int) or continuous (float)
Targets str, 'normal.' or name of the anomaly type Targets str, 'normal.' or name of the anomaly type
================ ========================================== ================ ==========================================
SA structure: SA structure:
================ ========================================== ================ ==========================================
Samples total 976158 Samples total 976158
Dimensionality 41 Dimensionality 41
Features discrete (int) or continuous (float) Features discrete (int) or continuous (float)
Targets str, 'normal.' or name of the anomaly type Targets str, 'normal.' or name of the anomaly type
================ ========================================== ================ ==========================================
SF structure: SF structure:
================ ========================================== ================ ==========================================
Samples total 699691 Samples total 699691
Dimensionality 4 Dimensionality 4
Features discrete (int) or continuous (float) Features discrete (int) or continuous (float)
Targets str, 'normal.' or name of the anomaly type Targets str, 'normal.' or name of the anomaly type
================ ========================================== ================ ==========================================
http structure: http structure:
================ ========================================== ================ ==========================================
Samples total 619052 Samples total 619052
Dimensionality 3 Dimensionality 3
Features discrete (int) or continuous (float) Features discrete (int) or continuous (float)
Targets str, 'normal.' or name of the anomaly type Targets str, 'normal.' or name of the anomaly type
================ ========================================== ================ ==========================================
smtp structure: smtp structure:
================ ========================================== ================ ==========================================
Samples total 95373 Samples total 95373
Dimensionality 3 Dimensionality 3
Features discrete (int) or continuous (float) Features discrete (int) or continuous (float)
Targets str, 'normal.' or name of the anomaly type Targets str, 'normal.' or name of the anomaly type
================ ========================================== ================ ==========================================
:func:`sklearn.datasets.fetch_kddcup99` will load the kddcup99 dataset; it :func:`sklearn.datasets.fetch_kddcup99` will load the kddcup99 dataset; it
returns a dictionary-like object with the feature matrix in the ``data`` member returns a dictionary-like object with the feature matrix in the ``data`` member
and the target values in ``target``. The "as_frame" optional argument converts and the target values in ``target``. The "as_frame" optional argument converts
``data`` into a pandas DataFrame and ``target`` into a pandas Series. The ``data`` into a pandas DataFrame and ``target`` into a pandas Series. The
dataset will be downloaded from the web if necessary. dataset will be downloaded from the web if necessary.
.. rubric:: References .. rubric:: References
.. [2] Analysis and Results of the 1999 DARPA Off-Line Intrusion .. [2] Analysis and Results of the 1999 DARPA Off-Line Intrusion
Detection Evaluation, Richard Lippmann, Joshua W. Haines, Detection Evaluation, Richard Lippmann, Joshua W. Haines,
David J. Fried, Jonathan Korba, Kumar Das. David J. Fried, Jonathan Korba, Kumar Das.
.. [3] K. Yamanishi, J.-I. Takeuchi, G. Williams, and P. Milne. Online .. [3] K. Yamanishi, J.-I. Takeuchi, G. Williams, and P. Milne. Online
unsupervised outlier detection using finite mixtures with unsupervised outlier detection using finite mixtures with
discounting learning algorithms. In Proceedings of the sixth discounting learning algorithms. In Proceedings of the sixth
ACM SIGKDD international conference on Knowledge discovery ACM SIGKDD international conference on Knowledge discovery
and data mining, pages 320-324. ACM Press, 2000. and data mining, pages 320-324. ACM Press, 2000.
@@ -1,124 +1,124 @@
.. _labeled_faces_in_the_wild_dataset: .. _labeled_faces_in_the_wild_dataset:
The Labeled Faces in the Wild face recognition dataset The Labeled Faces in the Wild face recognition dataset
------------------------------------------------------ ------------------------------------------------------
This dataset is a collection of JPEG pictures of famous people collected This dataset is a collection of JPEG pictures of famous people collected
over the internet, and the details are available on the Kaggle website: over the internet, and the details are available on the Kaggle website:
https://www.kaggle.com/datasets/jessicali9530/lfw-dataset https://www.kaggle.com/datasets/jessicali9530/lfw-dataset
Each picture is centered on a single face. The typical task is called Each picture is centered on a single face. The typical task is called
Face Verification: given a pair of two pictures, a binary classifier Face Verification: given a pair of two pictures, a binary classifier
must predict whether the two images are from the same person. must predict whether the two images are from the same person.
An alternative task, Face Recognition or Face Identification is: An alternative task, Face Recognition or Face Identification is:
given the picture of the face of an unknown person, identify the name given the picture of the face of an unknown person, identify the name
of the person by referring to a gallery of previously seen pictures of of the person by referring to a gallery of previously seen pictures of
identified persons. identified persons.
Both Face Verification and Face Recognition are tasks that are typically Both Face Verification and Face Recognition are tasks that are typically
performed on the output of a model trained to perform Face Detection. The performed on the output of a model trained to perform Face Detection. The
most popular model for Face Detection is called Viola-Jones and is most popular model for Face Detection is called Viola-Jones and is
implemented in the OpenCV library. The LFW faces were extracted by this implemented in the OpenCV library. The LFW faces were extracted by this
face detector from various online websites. face detector from various online websites.
**Data Set Characteristics:** **Data Set Characteristics:**
================= ======================= ================= =======================
Classes 5749 Classes 5749
Samples total 13233 Samples total 13233
Dimensionality 5828 Dimensionality 5828
Features real, between 0 and 255 Features real, between 0 and 255
================= ======================= ================= =======================
.. dropdown:: Usage .. dropdown:: Usage
``scikit-learn`` provides two loaders that will automatically download, ``scikit-learn`` provides two loaders that will automatically download,
cache, parse the metadata files, decode the jpeg and convert the cache, parse the metadata files, decode the jpeg and convert the
interesting slices into memmapped numpy arrays. This dataset size is more interesting slices into memmapped numpy arrays. This dataset size is more
than 200 MB. The first load typically takes more than a couple of minutes than 200 MB. The first load typically takes more than a couple of minutes
to fully decode the relevant part of the JPEG files into numpy arrays. If to fully decode the relevant part of the JPEG files into numpy arrays. If
the dataset has been loaded once, the following times the loading times the dataset has been loaded once, the following times the loading times
less than 200ms by using a memmapped version memoized on the disk in the less than 200ms by using a memmapped version memoized on the disk in the
``~/scikit_learn_data/lfw_home/`` folder using ``joblib``. ``~/scikit_learn_data/lfw_home/`` folder using ``joblib``.
The first loader is used for the Face Identification task: a multi-class The first loader is used for the Face Identification task: a multi-class
classification task (hence supervised learning):: classification task (hence supervised learning)::
>>> from sklearn.datasets import fetch_lfw_people >>> from sklearn.datasets import fetch_lfw_people
>>> lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4) >>> lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)
>>> for name in lfw_people.target_names: >>> for name in lfw_people.target_names:
... print(name) ... print(name)
... ...
Ariel Sharon Ariel Sharon
Colin Powell Colin Powell
Donald Rumsfeld Donald Rumsfeld
George W Bush George W Bush
Gerhard Schroeder Gerhard Schroeder
Hugo Chavez Hugo Chavez
Tony Blair Tony Blair
The default slice is a rectangular shape around the face, removing The default slice is a rectangular shape around the face, removing
most of the background:: most of the background::
>>> lfw_people.data.dtype >>> lfw_people.data.dtype
dtype('float32') dtype('float32')
>>> lfw_people.data.shape >>> lfw_people.data.shape
(1288, 1850) (1288, 1850)
>>> lfw_people.images.shape >>> lfw_people.images.shape
(1288, 50, 37) (1288, 50, 37)
Each of the ``1140`` faces is assigned to a single person id in the ``target`` Each of the ``1140`` faces is assigned to a single person id in the ``target``
array:: array::
>>> lfw_people.target.shape >>> lfw_people.target.shape
(1288,) (1288,)
>>> list(lfw_people.target[:10]) >>> list(lfw_people.target[:10])
[5, 6, 3, 1, 0, 1, 3, 4, 3, 0] [5, 6, 3, 1, 0, 1, 3, 4, 3, 0]
The second loader is typically used for the face verification task: each sample The second loader is typically used for the face verification task: each sample
is a pair of two picture belonging or not to the same person:: is a pair of two picture belonging or not to the same person::
>>> from sklearn.datasets import fetch_lfw_pairs >>> from sklearn.datasets import fetch_lfw_pairs
>>> lfw_pairs_train = fetch_lfw_pairs(subset='train') >>> lfw_pairs_train = fetch_lfw_pairs(subset='train')
>>> list(lfw_pairs_train.target_names) >>> list(lfw_pairs_train.target_names)
['Different persons', 'Same person'] ['Different persons', 'Same person']
>>> lfw_pairs_train.pairs.shape >>> lfw_pairs_train.pairs.shape
(2200, 2, 62, 47) (2200, 2, 62, 47)
>>> lfw_pairs_train.data.shape >>> lfw_pairs_train.data.shape
(2200, 5828) (2200, 5828)
>>> lfw_pairs_train.target.shape >>> lfw_pairs_train.target.shape
(2200,) (2200,)
Both for the :func:`sklearn.datasets.fetch_lfw_people` and Both for the :func:`sklearn.datasets.fetch_lfw_people` and
:func:`sklearn.datasets.fetch_lfw_pairs` function it is :func:`sklearn.datasets.fetch_lfw_pairs` function it is
possible to get an additional dimension with the RGB color channels by possible to get an additional dimension with the RGB color channels by
passing ``color=True``, in that case the shape will be passing ``color=True``, in that case the shape will be
``(2200, 2, 62, 47, 3)``. ``(2200, 2, 62, 47, 3)``.
The :func:`sklearn.datasets.fetch_lfw_pairs` datasets is subdivided into The :func:`sklearn.datasets.fetch_lfw_pairs` datasets is subdivided into
3 subsets: the development ``train`` set, the development ``test`` set and 3 subsets: the development ``train`` set, the development ``test`` set and
an evaluation ``10_folds`` set meant to compute performance metrics using a an evaluation ``10_folds`` set meant to compute performance metrics using a
10-folds cross validation scheme. 10-folds cross validation scheme.
.. rubric:: References .. rubric:: References
* `Labeled Faces in the Wild: A Database for Studying Face Recognition * `Labeled Faces in the Wild: A Database for Studying Face Recognition
in Unconstrained Environments. in Unconstrained Environments.
<https://people.cs.umass.edu/~elm/papers/lfw.pdf>`_ <https://people.cs.umass.edu/~elm/papers/lfw.pdf>`_
Gary B. Huang, Manu Ramesh, Tamara Berg, and Erik Learned-Miller. Gary B. Huang, Manu Ramesh, Tamara Berg, and Erik Learned-Miller.
University of Massachusetts, Amherst, Technical Report 07-49, October, 2007. University of Massachusetts, Amherst, Technical Report 07-49, October, 2007.
.. rubric:: Examples .. rubric:: Examples
* :ref:`sphx_glr_auto_examples_applications_plot_face_recognition.py` * :ref:`sphx_glr_auto_examples_applications_plot_face_recognition.py`
@@ -1,24 +1,24 @@
.. _linnerrud_dataset: .. _linnerrud_dataset:
Linnerrud dataset Linnerrud dataset
----------------- -----------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 20 :Number of Instances: 20
:Number of Attributes: 3 :Number of Attributes: 3
:Missing Attribute Values: None :Missing Attribute Values: None
The Linnerud dataset is a multi-output regression dataset. It consists of three The Linnerud dataset is a multi-output regression dataset. It consists of three
exercise (data) and three physiological (target) variables collected from exercise (data) and three physiological (target) variables collected from
twenty middle-aged men in a fitness club: twenty middle-aged men in a fitness club:
- *physiological* - CSV containing 20 observations on 3 physiological variables: - *physiological* - CSV containing 20 observations on 3 physiological variables:
Weight, Waist and Pulse. Weight, Waist and Pulse.
- *exercise* - CSV containing 20 observations on 3 exercise variables: - *exercise* - CSV containing 20 observations on 3 exercise variables:
Chins, Situps and Jumps. Chins, Situps and Jumps.
.. dropdown:: References .. dropdown:: References
* Tenenhaus, M. (1998). La regression PLS: theorie et pratique. Paris: * Tenenhaus, M. (1998). La regression PLS: theorie et pratique. Paris:
Editions Technic. Editions Technic.
@@ -1,44 +1,44 @@
.. _olivetti_faces_dataset: .. _olivetti_faces_dataset:
The Olivetti faces dataset The Olivetti faces dataset
-------------------------- --------------------------
`This dataset contains a set of face images`_ taken between April 1992 and `This dataset contains a set of face images`_ taken between April 1992 and
April 1994 at AT&T Laboratories Cambridge. The April 1994 at AT&T Laboratories Cambridge. The
:func:`sklearn.datasets.fetch_olivetti_faces` function is the data :func:`sklearn.datasets.fetch_olivetti_faces` function is the data
fetching / caching function that downloads the data fetching / caching function that downloads the data
archive from AT&T. archive from AT&T.
.. _This dataset contains a set of face images: https://cam-orl.co.uk/facedatabase.html .. _This dataset contains a set of face images: https://cam-orl.co.uk/facedatabase.html
As described on the original website: As described on the original website:
There are ten different images of each of 40 distinct subjects. For some There are ten different images of each of 40 distinct subjects. For some
subjects, the images were taken at different times, varying the lighting, subjects, the images were taken at different times, varying the lighting,
facial expressions (open / closed eyes, smiling / not smiling) and facial facial expressions (open / closed eyes, smiling / not smiling) and facial
details (glasses / no glasses). All the images were taken against a dark details (glasses / no glasses). All the images were taken against a dark
homogeneous background with the subjects in an upright, frontal position homogeneous background with the subjects in an upright, frontal position
(with tolerance for some side movement). (with tolerance for some side movement).
**Data Set Characteristics:** **Data Set Characteristics:**
================= ===================== ================= =====================
Classes 40 Classes 40
Samples total 400 Samples total 400
Dimensionality 4096 Dimensionality 4096
Features real, between 0 and 1 Features real, between 0 and 1
================= ===================== ================= =====================
The image is quantized to 256 grey levels and stored as unsigned 8-bit The image is quantized to 256 grey levels and stored as unsigned 8-bit
integers; the loader will convert these to floating point values on the integers; the loader will convert these to floating point values on the
interval [0, 1], which are easier to work with for many algorithms. interval [0, 1], which are easier to work with for many algorithms.
The "target" for this database is an integer from 0 to 39 indicating the The "target" for this database is an integer from 0 to 39 indicating the
identity of the person pictured; however, with only 10 examples per class, this identity of the person pictured; however, with only 10 examples per class, this
relatively small dataset is more interesting from an unsupervised or relatively small dataset is more interesting from an unsupervised or
semi-supervised perspective. semi-supervised perspective.
The original dataset consisted of 92 x 112, while the version available here The original dataset consisted of 92 x 112, while the version available here
consists of 64x64 images. consists of 64x64 images.
When using these images, please give credit to AT&T Laboratories Cambridge. When using these images, please give credit to AT&T Laboratories Cambridge.
@@ -1,72 +1,72 @@
.. _rcv1_dataset: .. _rcv1_dataset:
RCV1 dataset RCV1 dataset
------------ ------------
Reuters Corpus Volume I (RCV1) is an archive of over 800,000 manually Reuters Corpus Volume I (RCV1) is an archive of over 800,000 manually
categorized newswire stories made available by Reuters, Ltd. for research categorized newswire stories made available by Reuters, Ltd. for research
purposes. The dataset is extensively described in [1]_. purposes. The dataset is extensively described in [1]_.
**Data Set Characteristics:** **Data Set Characteristics:**
============== ===================== ============== =====================
Classes 103 Classes 103
Samples total 804414 Samples total 804414
Dimensionality 47236 Dimensionality 47236
Features real, between 0 and 1 Features real, between 0 and 1
============== ===================== ============== =====================
:func:`sklearn.datasets.fetch_rcv1` will load the following :func:`sklearn.datasets.fetch_rcv1` will load the following
version: RCV1-v2, vectors, full sets, topics multilabels:: version: RCV1-v2, vectors, full sets, topics multilabels::
>>> from sklearn.datasets import fetch_rcv1 >>> from sklearn.datasets import fetch_rcv1
>>> rcv1 = fetch_rcv1() >>> rcv1 = fetch_rcv1()
It returns a dictionary-like object, with the following attributes: It returns a dictionary-like object, with the following attributes:
``data``: ``data``:
The feature matrix is a scipy CSR sparse matrix, with 804414 samples and The feature matrix is a scipy CSR sparse matrix, with 804414 samples and
47236 features. Non-zero values contains cosine-normalized, log TF-IDF vectors. 47236 features. Non-zero values contains cosine-normalized, log TF-IDF vectors.
A nearly chronological split is proposed in [1]_: The first 23149 samples are A nearly chronological split is proposed in [1]_: The first 23149 samples are
the training set. The last 781265 samples are the testing set. This follows the training set. The last 781265 samples are the testing set. This follows
the official LYRL2004 chronological split. The array has 0.16% of non zero the official LYRL2004 chronological split. The array has 0.16% of non zero
values:: values::
>>> rcv1.data.shape >>> rcv1.data.shape
(804414, 47236) (804414, 47236)
``target``: ``target``:
The target values are stored in a scipy CSR sparse matrix, with 804414 samples The target values are stored in a scipy CSR sparse matrix, with 804414 samples
and 103 categories. Each sample has a value of 1 in its categories, and 0 in and 103 categories. Each sample has a value of 1 in its categories, and 0 in
others. The array has 3.15% of non zero values:: others. The array has 3.15% of non zero values::
>>> rcv1.target.shape >>> rcv1.target.shape
(804414, 103) (804414, 103)
``sample_id``: ``sample_id``:
Each sample can be identified by its ID, ranging (with gaps) from 2286 Each sample can be identified by its ID, ranging (with gaps) from 2286
to 810596:: to 810596::
>>> rcv1.sample_id[:3] >>> rcv1.sample_id[:3]
array([2286, 2287, 2288], dtype=uint32) array([2286, 2287, 2288], dtype=uint32)
``target_names``: ``target_names``:
The target values are the topics of each sample. Each sample belongs to at The target values are the topics of each sample. Each sample belongs to at
least one topic, and to up to 17 topics. There are 103 topics, each least one topic, and to up to 17 topics. There are 103 topics, each
represented by a string. Their corpus frequencies span five orders of represented by a string. Their corpus frequencies span five orders of
magnitude, from 5 occurrences for 'GMIL', to 381327 for 'CCAT':: magnitude, from 5 occurrences for 'GMIL', to 381327 for 'CCAT'::
>>> rcv1.target_names[:3].tolist() # doctest: +SKIP >>> rcv1.target_names[:3].tolist() # doctest: +SKIP
['E11', 'ECAT', 'M11'] ['E11', 'ECAT', 'M11']
The dataset will be downloaded from the `rcv1 homepage`_ if necessary. The dataset will be downloaded from the `rcv1 homepage`_ if necessary.
The compressed size is about 656 MB. The compressed size is about 656 MB.
.. _rcv1 homepage: http://jmlr.csail.mit.edu/papers/volume5/lewis04a/ .. _rcv1 homepage: http://jmlr.csail.mit.edu/papers/volume5/lewis04a/
.. rubric:: References .. rubric:: References
.. [1] Lewis, D. D., Yang, Y., Rose, T. G., & Li, F. (2004). .. [1] Lewis, D. D., Yang, Y., Rose, T. G., & Li, F. (2004).
RCV1: A new benchmark collection for text categorization research. RCV1: A new benchmark collection for text categorization research.
The Journal of Machine Learning Research, 5, 361-397. The Journal of Machine Learning Research, 5, 361-397.
@@ -1,40 +1,40 @@
.. _species_distribution_dataset: .. _species_distribution_dataset:
Species distribution dataset Species distribution dataset
---------------------------- ----------------------------
This dataset represents the geographic distribution of two species in Central and This dataset represents the geographic distribution of two species in Central and
South America. The two species are: South America. The two species are:
- `"Bradypus variegatus" <http://www.iucnredlist.org/details/3038/0>`_ , - `"Bradypus variegatus" <http://www.iucnredlist.org/details/3038/0>`_ ,
the Brown-throated Sloth. the Brown-throated Sloth.
- `"Microryzomys minutus" <http://www.iucnredlist.org/details/13408/0>`_ , - `"Microryzomys minutus" <http://www.iucnredlist.org/details/13408/0>`_ ,
also known as the Forest Small Rice Rat, a rodent that lives in Peru, also known as the Forest Small Rice Rat, a rodent that lives in Peru,
Colombia, Ecuador, Peru, and Venezuela. Colombia, Ecuador, Peru, and Venezuela.
The dataset is not a typical dataset since a :class:`~sklearn.datasets.base.Bunch` The dataset is not a typical dataset since a :class:`~sklearn.datasets.base.Bunch`
containing the attributes `data` and `target` is not returned. Instead, we have containing the attributes `data` and `target` is not returned. Instead, we have
information allowing to create a "density" map of the different species. information allowing to create a "density" map of the different species.
The grid for the map can be built using the attributes `x_left_lower_corner`, The grid for the map can be built using the attributes `x_left_lower_corner`,
`y_left_lower_corner`, `Nx`, `Ny` and `grid_size`, which respectively correspond `y_left_lower_corner`, `Nx`, `Ny` and `grid_size`, which respectively correspond
to the x and y coordinates of the lower left corner of the grid, the number of to the x and y coordinates of the lower left corner of the grid, the number of
points along the x- and y-axis and the size of the step on the grid. points along the x- and y-axis and the size of the step on the grid.
The density at each location of the grid is contained in the `coverage` attribute. The density at each location of the grid is contained in the `coverage` attribute.
Finally, the `train` and `test` attributes contain information regarding the location Finally, the `train` and `test` attributes contain information regarding the location
of a species at a specific location. of a species at a specific location.
The dataset is provided by Phillips et. al. (2006). The dataset is provided by Phillips et. al. (2006).
.. rubric:: References .. rubric:: References
* `"Maximum entropy modeling of species geographic distributions" * `"Maximum entropy modeling of species geographic distributions"
<http://rob.schapire.net/papers/ecolmod.pdf>`_ S. J. Phillips, <http://rob.schapire.net/papers/ecolmod.pdf>`_ S. J. Phillips,
R. P. Anderson, R. E. Schapire - Ecological Modelling, 190:231-259, 2006. R. P. Anderson, R. E. Schapire - Ecological Modelling, 190:231-259, 2006.
.. rubric:: Examples .. rubric:: Examples
* :ref:`sphx_glr_auto_examples_applications_plot_species_distribution_modeling.py` * :ref:`sphx_glr_auto_examples_applications_plot_species_distribution_modeling.py`
@@ -1,248 +1,248 @@
.. _20newsgroups_dataset: .. _20newsgroups_dataset:
The 20 newsgroups text dataset The 20 newsgroups text dataset
------------------------------ ------------------------------
The 20 newsgroups dataset comprises around 18000 newsgroups posts on The 20 newsgroups dataset comprises around 18000 newsgroups posts on
20 topics split in two subsets: one for training (or development) 20 topics split in two subsets: one for training (or development)
and the other one for testing (or for performance evaluation). The split and the other one for testing (or for performance evaluation). The split
between the train and test set is based upon a messages posted before between the train and test set is based upon a messages posted before
and after a specific date. and after a specific date.
This module contains two loaders. The first one, This module contains two loaders. The first one,
:func:`sklearn.datasets.fetch_20newsgroups`, :func:`sklearn.datasets.fetch_20newsgroups`,
returns a list of the raw texts that can be fed to text feature returns a list of the raw texts that can be fed to text feature
extractors such as :class:`~sklearn.feature_extraction.text.CountVectorizer` extractors such as :class:`~sklearn.feature_extraction.text.CountVectorizer`
with custom parameters so as to extract feature vectors. with custom parameters so as to extract feature vectors.
The second one, :func:`sklearn.datasets.fetch_20newsgroups_vectorized`, The second one, :func:`sklearn.datasets.fetch_20newsgroups_vectorized`,
returns ready-to-use features, i.e., it is not necessary to use a feature returns ready-to-use features, i.e., it is not necessary to use a feature
extractor. extractor.
**Data Set Characteristics:** **Data Set Characteristics:**
================= ========== ================= ==========
Classes 20 Classes 20
Samples total 18846 Samples total 18846
Dimensionality 1 Dimensionality 1
Features text Features text
================= ========== ================= ==========
.. dropdown:: Usage .. dropdown:: Usage
The :func:`sklearn.datasets.fetch_20newsgroups` function is a data The :func:`sklearn.datasets.fetch_20newsgroups` function is a data
fetching / caching functions that downloads the data archive from fetching / caching functions that downloads the data archive from
the original `20 newsgroups website <http://people.csail.mit.edu/jrennie/20Newsgroups/>`__, the original `20 newsgroups website <http://people.csail.mit.edu/jrennie/20Newsgroups/>`__,
extracts the archive contents extracts the archive contents
in the ``~/scikit_learn_data/20news_home`` folder and calls the in the ``~/scikit_learn_data/20news_home`` folder and calls the
:func:`sklearn.datasets.load_files` on either the training or :func:`sklearn.datasets.load_files` on either the training or
testing set folder, or both of them:: testing set folder, or both of them::
>>> from sklearn.datasets import fetch_20newsgroups >>> from sklearn.datasets import fetch_20newsgroups
>>> newsgroups_train = fetch_20newsgroups(subset='train') >>> newsgroups_train = fetch_20newsgroups(subset='train')
>>> from pprint import pprint >>> from pprint import pprint
>>> pprint(list(newsgroups_train.target_names)) >>> pprint(list(newsgroups_train.target_names))
['alt.atheism', ['alt.atheism',
'comp.graphics', 'comp.graphics',
'comp.os.ms-windows.misc', 'comp.os.ms-windows.misc',
'comp.sys.ibm.pc.hardware', 'comp.sys.ibm.pc.hardware',
'comp.sys.mac.hardware', 'comp.sys.mac.hardware',
'comp.windows.x', 'comp.windows.x',
'misc.forsale', 'misc.forsale',
'rec.autos', 'rec.autos',
'rec.motorcycles', 'rec.motorcycles',
'rec.sport.baseball', 'rec.sport.baseball',
'rec.sport.hockey', 'rec.sport.hockey',
'sci.crypt', 'sci.crypt',
'sci.electronics', 'sci.electronics',
'sci.med', 'sci.med',
'sci.space', 'sci.space',
'soc.religion.christian', 'soc.religion.christian',
'talk.politics.guns', 'talk.politics.guns',
'talk.politics.mideast', 'talk.politics.mideast',
'talk.politics.misc', 'talk.politics.misc',
'talk.religion.misc'] 'talk.religion.misc']
The real data lies in the ``filenames`` and ``target`` attributes. The target The real data lies in the ``filenames`` and ``target`` attributes. The target
attribute is the integer index of the category:: attribute is the integer index of the category::
>>> newsgroups_train.filenames.shape >>> newsgroups_train.filenames.shape
(11314,) (11314,)
>>> newsgroups_train.target.shape >>> newsgroups_train.target.shape
(11314,) (11314,)
>>> newsgroups_train.target[:10] >>> newsgroups_train.target[:10]
array([ 7, 4, 4, 1, 14, 16, 13, 3, 2, 4]) array([ 7, 4, 4, 1, 14, 16, 13, 3, 2, 4])
It is possible to load only a sub-selection of the categories by passing the It is possible to load only a sub-selection of the categories by passing the
list of the categories to load to the list of the categories to load to the
:func:`sklearn.datasets.fetch_20newsgroups` function:: :func:`sklearn.datasets.fetch_20newsgroups` function::
>>> cats = ['alt.atheism', 'sci.space'] >>> cats = ['alt.atheism', 'sci.space']
>>> newsgroups_train = fetch_20newsgroups(subset='train', categories=cats) >>> newsgroups_train = fetch_20newsgroups(subset='train', categories=cats)
>>> list(newsgroups_train.target_names) >>> list(newsgroups_train.target_names)
['alt.atheism', 'sci.space'] ['alt.atheism', 'sci.space']
>>> newsgroups_train.filenames.shape >>> newsgroups_train.filenames.shape
(1073,) (1073,)
>>> newsgroups_train.target.shape >>> newsgroups_train.target.shape
(1073,) (1073,)
>>> newsgroups_train.target[:10] >>> newsgroups_train.target[:10]
array([0, 1, 1, 1, 0, 1, 1, 0, 0, 0]) array([0, 1, 1, 1, 0, 1, 1, 0, 0, 0])
.. dropdown:: Converting text to vectors .. dropdown:: Converting text to vectors
In order to feed predictive or clustering models with the text data, In order to feed predictive or clustering models with the text data,
one first need to turn the text into vectors of numerical values suitable one first need to turn the text into vectors of numerical values suitable
for statistical analysis. This can be achieved with the utilities of the for statistical analysis. This can be achieved with the utilities of the
``sklearn.feature_extraction.text`` as demonstrated in the following ``sklearn.feature_extraction.text`` as demonstrated in the following
example that extract `TF-IDF <https://en.wikipedia.org/wiki/Tf-idf>`__ vectors example that extract `TF-IDF <https://en.wikipedia.org/wiki/Tf-idf>`__ vectors
of unigram tokens from a subset of 20news:: of unigram tokens from a subset of 20news::
>>> from sklearn.feature_extraction.text import TfidfVectorizer >>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> categories = ['alt.atheism', 'talk.religion.misc', >>> categories = ['alt.atheism', 'talk.religion.misc',
... 'comp.graphics', 'sci.space'] ... 'comp.graphics', 'sci.space']
>>> newsgroups_train = fetch_20newsgroups(subset='train', >>> newsgroups_train = fetch_20newsgroups(subset='train',
... categories=categories) ... categories=categories)
>>> vectorizer = TfidfVectorizer() >>> vectorizer = TfidfVectorizer()
>>> vectors = vectorizer.fit_transform(newsgroups_train.data) >>> vectors = vectorizer.fit_transform(newsgroups_train.data)
>>> vectors.shape >>> vectors.shape
(2034, 34118) (2034, 34118)
The extracted TF-IDF vectors are very sparse, with an average of 159 non-zero The extracted TF-IDF vectors are very sparse, with an average of 159 non-zero
components by sample in a more than 30000-dimensional space components by sample in a more than 30000-dimensional space
(less than .5% non-zero features):: (less than .5% non-zero features)::
>>> vectors.nnz / float(vectors.shape[0]) >>> vectors.nnz / float(vectors.shape[0])
159.01327... 159.01327...
:func:`sklearn.datasets.fetch_20newsgroups_vectorized` is a function which :func:`sklearn.datasets.fetch_20newsgroups_vectorized` is a function which
returns ready-to-use token counts features instead of file names. returns ready-to-use token counts features instead of file names.
.. dropdown:: Filtering text for more realistic training .. dropdown:: Filtering text for more realistic training
It is easy for a classifier to overfit on particular things that appear in the It is easy for a classifier to overfit on particular things that appear in the
20 Newsgroups data, such as newsgroup headers. Many classifiers achieve very 20 Newsgroups data, such as newsgroup headers. Many classifiers achieve very
high F-scores, but their results would not generalize to other documents that high F-scores, but their results would not generalize to other documents that
aren't from this window of time. aren't from this window of time.
For example, let's look at the results of a multinomial Naive Bayes classifier, For example, let's look at the results of a multinomial Naive Bayes classifier,
which is fast to train and achieves a decent F-score:: which is fast to train and achieves a decent F-score::
>>> from sklearn.naive_bayes import MultinomialNB >>> from sklearn.naive_bayes import MultinomialNB
>>> from sklearn import metrics >>> from sklearn import metrics
>>> newsgroups_test = fetch_20newsgroups(subset='test', >>> newsgroups_test = fetch_20newsgroups(subset='test',
... categories=categories) ... categories=categories)
>>> vectors_test = vectorizer.transform(newsgroups_test.data) >>> vectors_test = vectorizer.transform(newsgroups_test.data)
>>> clf = MultinomialNB(alpha=.01) >>> clf = MultinomialNB(alpha=.01)
>>> clf.fit(vectors, newsgroups_train.target) >>> clf.fit(vectors, newsgroups_train.target)
MultinomialNB(alpha=0.01, class_prior=None, fit_prior=True) MultinomialNB(alpha=0.01, class_prior=None, fit_prior=True)
>>> pred = clf.predict(vectors_test) >>> pred = clf.predict(vectors_test)
>>> metrics.f1_score(newsgroups_test.target, pred, average='macro') >>> metrics.f1_score(newsgroups_test.target, pred, average='macro')
0.88213... 0.88213...
(The example :ref:`sphx_glr_auto_examples_text_plot_document_classification_20newsgroups.py` shuffles (The example :ref:`sphx_glr_auto_examples_text_plot_document_classification_20newsgroups.py` shuffles
the training and test data, instead of segmenting by time, and in that case the training and test data, instead of segmenting by time, and in that case
multinomial Naive Bayes gets a much higher F-score of 0.88. Are you suspicious multinomial Naive Bayes gets a much higher F-score of 0.88. Are you suspicious
yet of what's going on inside this classifier?) yet of what's going on inside this classifier?)
Let's take a look at what the most informative features are: Let's take a look at what the most informative features are:
>>> import numpy as np >>> import numpy as np
>>> def show_top10(classifier, vectorizer, categories): >>> def show_top10(classifier, vectorizer, categories):
... feature_names = vectorizer.get_feature_names_out() ... feature_names = vectorizer.get_feature_names_out()
... for i, category in enumerate(categories): ... for i, category in enumerate(categories):
... top10 = np.argsort(classifier.coef_[i])[-10:] ... top10 = np.argsort(classifier.coef_[i])[-10:]
... print("%s: %s" % (category, " ".join(feature_names[top10]))) ... print("%s: %s" % (category, " ".join(feature_names[top10])))
... ...
>>> show_top10(clf, vectorizer, newsgroups_train.target_names) >>> show_top10(clf, vectorizer, newsgroups_train.target_names)
alt.atheism: edu it and in you that is of to the alt.atheism: edu it and in you that is of to the
comp.graphics: edu in graphics it is for and of to the comp.graphics: edu in graphics it is for and of to the
sci.space: edu it that is in and space to of the sci.space: edu it that is in and space to of the
talk.religion.misc: not it you in is that and to of the talk.religion.misc: not it you in is that and to of the
You can now see many things that these features have overfit to: You can now see many things that these features have overfit to:
- Almost every group is distinguished by whether headers such as - Almost every group is distinguished by whether headers such as
``NNTP-Posting-Host:`` and ``Distribution:`` appear more or less often. ``NNTP-Posting-Host:`` and ``Distribution:`` appear more or less often.
- Another significant feature involves whether the sender is affiliated with - Another significant feature involves whether the sender is affiliated with
a university, as indicated either by their headers or their signature. a university, as indicated either by their headers or their signature.
- The word "article" is a significant feature, based on how often people quote - The word "article" is a significant feature, based on how often people quote
previous posts like this: "In article [article ID], [name] <[e-mail address]> previous posts like this: "In article [article ID], [name] <[e-mail address]>
wrote:" wrote:"
- Other features match the names and e-mail addresses of particular people who - Other features match the names and e-mail addresses of particular people who
were posting at the time. were posting at the time.
With such an abundance of clues that distinguish newsgroups, the classifiers With such an abundance of clues that distinguish newsgroups, the classifiers
barely have to identify topics from text at all, and they all perform at the barely have to identify topics from text at all, and they all perform at the
same high level. same high level.
For this reason, the functions that load 20 Newsgroups data provide a For this reason, the functions that load 20 Newsgroups data provide a
parameter called **remove**, telling it what kinds of information to strip out parameter called **remove**, telling it what kinds of information to strip out
of each file. **remove** should be a tuple containing any subset of of each file. **remove** should be a tuple containing any subset of
``('headers', 'footers', 'quotes')``, telling it to remove headers, signature ``('headers', 'footers', 'quotes')``, telling it to remove headers, signature
blocks, and quotation blocks respectively. blocks, and quotation blocks respectively.
>>> newsgroups_test = fetch_20newsgroups(subset='test', >>> newsgroups_test = fetch_20newsgroups(subset='test',
... remove=('headers', 'footers', 'quotes'), ... remove=('headers', 'footers', 'quotes'),
... categories=categories) ... categories=categories)
>>> vectors_test = vectorizer.transform(newsgroups_test.data) >>> vectors_test = vectorizer.transform(newsgroups_test.data)
>>> pred = clf.predict(vectors_test) >>> pred = clf.predict(vectors_test)
>>> metrics.f1_score(pred, newsgroups_test.target, average='macro') >>> metrics.f1_score(pred, newsgroups_test.target, average='macro')
0.77310... 0.77310...
This classifier lost over a lot of its F-score, just because we removed This classifier lost over a lot of its F-score, just because we removed
metadata that has little to do with topic classification. metadata that has little to do with topic classification.
It loses even more if we also strip this metadata from the training data: It loses even more if we also strip this metadata from the training data:
>>> newsgroups_train = fetch_20newsgroups(subset='train', >>> newsgroups_train = fetch_20newsgroups(subset='train',
... remove=('headers', 'footers', 'quotes'), ... remove=('headers', 'footers', 'quotes'),
... categories=categories) ... categories=categories)
>>> vectors = vectorizer.fit_transform(newsgroups_train.data) >>> vectors = vectorizer.fit_transform(newsgroups_train.data)
>>> clf = MultinomialNB(alpha=.01) >>> clf = MultinomialNB(alpha=.01)
>>> clf.fit(vectors, newsgroups_train.target) >>> clf.fit(vectors, newsgroups_train.target)
MultinomialNB(alpha=0.01, class_prior=None, fit_prior=True) MultinomialNB(alpha=0.01, class_prior=None, fit_prior=True)
>>> vectors_test = vectorizer.transform(newsgroups_test.data) >>> vectors_test = vectorizer.transform(newsgroups_test.data)
>>> pred = clf.predict(vectors_test) >>> pred = clf.predict(vectors_test)
>>> metrics.f1_score(newsgroups_test.target, pred, average='macro') >>> metrics.f1_score(newsgroups_test.target, pred, average='macro')
0.76995... 0.76995...
Some other classifiers cope better with this harder version of the task. Try the Some other classifiers cope better with this harder version of the task. Try the
:ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_text_feature_extraction.py` :ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_text_feature_extraction.py`
example with and without the `remove` option to compare the results. example with and without the `remove` option to compare the results.
.. rubric:: Data Considerations .. rubric:: Data Considerations
The Cleveland Indians is a major league baseball team based in Cleveland, The Cleveland Indians is a major league baseball team based in Cleveland,
Ohio, USA. In December 2020, it was reported that "After several months of Ohio, USA. In December 2020, it was reported that "After several months of
discussion sparked by the death of George Floyd and a national reckoning over discussion sparked by the death of George Floyd and a national reckoning over
race and colonialism, the Cleveland Indians have decided to change their race and colonialism, the Cleveland Indians have decided to change their
name." Team owner Paul Dolan "did make it clear that the team will not make name." Team owner Paul Dolan "did make it clear that the team will not make
its informal nickname -- the Tribe -- its new team name." "It's not going to its informal nickname -- the Tribe -- its new team name." "It's not going to
be a half-step away from the Indians," Dolan said."We will not have a Native be a half-step away from the Indians," Dolan said."We will not have a Native
American-themed name." American-themed name."
https://www.mlb.com/news/cleveland-indians-team-name-change https://www.mlb.com/news/cleveland-indians-team-name-change
.. rubric:: Recommendation .. rubric:: Recommendation
- When evaluating text classifiers on the 20 Newsgroups data, you - When evaluating text classifiers on the 20 Newsgroups data, you
should strip newsgroup-related metadata. In scikit-learn, you can do this should strip newsgroup-related metadata. In scikit-learn, you can do this
by setting ``remove=('headers', 'footers', 'quotes')``. The F-score will be by setting ``remove=('headers', 'footers', 'quotes')``. The F-score will be
lower because it is more realistic. lower because it is more realistic.
- This text dataset contains data which may be inappropriate for certain NLP - This text dataset contains data which may be inappropriate for certain NLP
applications. An example is listed in the "Data Considerations" section applications. An example is listed in the "Data Considerations" section
above. The challenge with using current text datasets in NLP for tasks such above. The challenge with using current text datasets in NLP for tasks such
as sentence completion, clustering, and other applications is that text as sentence completion, clustering, and other applications is that text
that is culturally biased and inflammatory will propagate biases. This that is culturally biased and inflammatory will propagate biases. This
should be taken into consideration when using the dataset, reviewing the should be taken into consideration when using the dataset, reviewing the
output, and the bias should be documented. output, and the bias should be documented.
.. rubric:: Examples .. rubric:: Examples
* :ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_text_feature_extraction.py` * :ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_text_feature_extraction.py`
* :ref:`sphx_glr_auto_examples_text_plot_document_classification_20newsgroups.py` * :ref:`sphx_glr_auto_examples_text_plot_document_classification_20newsgroups.py`
* :ref:`sphx_glr_auto_examples_text_plot_hashing_vs_dict_vectorizer.py` * :ref:`sphx_glr_auto_examples_text_plot_hashing_vs_dict_vectorizer.py`
* :ref:`sphx_glr_auto_examples_text_plot_document_clustering.py` * :ref:`sphx_glr_auto_examples_text_plot_document_clustering.py`
@@ -1,94 +1,94 @@
.. _wine_dataset: .. _wine_dataset:
Wine recognition dataset Wine recognition dataset
------------------------ ------------------------
**Data Set Characteristics:** **Data Set Characteristics:**
:Number of Instances: 178 :Number of Instances: 178
:Number of Attributes: 13 numeric, predictive attributes and the class :Number of Attributes: 13 numeric, predictive attributes and the class
:Attribute Information: :Attribute Information:
- Alcohol - Alcohol
- Malic acid - Malic acid
- Ash - Ash
- Alcalinity of ash - Alcalinity of ash
- Magnesium - Magnesium
- Total phenols - Total phenols
- Flavanoids - Flavanoids
- Nonflavanoid phenols - Nonflavanoid phenols
- Proanthocyanins - Proanthocyanins
- Color intensity - Color intensity
- Hue - Hue
- OD280/OD315 of diluted wines - OD280/OD315 of diluted wines
- Proline - Proline
- class: - class:
- class_0 - class_0
- class_1 - class_1
- class_2 - class_2
:Summary Statistics: :Summary Statistics:
============================= ==== ===== ======= ===== ============================= ==== ===== ======= =====
Min Max Mean SD Min Max Mean SD
============================= ==== ===== ======= ===== ============================= ==== ===== ======= =====
Alcohol: 11.0 14.8 13.0 0.8 Alcohol: 11.0 14.8 13.0 0.8
Malic Acid: 0.74 5.80 2.34 1.12 Malic Acid: 0.74 5.80 2.34 1.12
Ash: 1.36 3.23 2.36 0.27 Ash: 1.36 3.23 2.36 0.27
Alcalinity of Ash: 10.6 30.0 19.5 3.3 Alcalinity of Ash: 10.6 30.0 19.5 3.3
Magnesium: 70.0 162.0 99.7 14.3 Magnesium: 70.0 162.0 99.7 14.3
Total Phenols: 0.98 3.88 2.29 0.63 Total Phenols: 0.98 3.88 2.29 0.63
Flavanoids: 0.34 5.08 2.03 1.00 Flavanoids: 0.34 5.08 2.03 1.00
Nonflavanoid Phenols: 0.13 0.66 0.36 0.12 Nonflavanoid Phenols: 0.13 0.66 0.36 0.12
Proanthocyanins: 0.41 3.58 1.59 0.57 Proanthocyanins: 0.41 3.58 1.59 0.57
Colour Intensity: 1.3 13.0 5.1 2.3 Colour Intensity: 1.3 13.0 5.1 2.3
Hue: 0.48 1.71 0.96 0.23 Hue: 0.48 1.71 0.96 0.23
OD280/OD315 of diluted wines: 1.27 4.00 2.61 0.71 OD280/OD315 of diluted wines: 1.27 4.00 2.61 0.71
Proline: 278 1680 746 315 Proline: 278 1680 746 315
============================= ==== ===== ======= ===== ============================= ==== ===== ======= =====
:Missing Attribute Values: None :Missing Attribute Values: None
:Class Distribution: class_0 (59), class_1 (71), class_2 (48) :Class Distribution: class_0 (59), class_1 (71), class_2 (48)
:Creator: R.A. Fisher :Creator: R.A. Fisher
:Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov) :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
:Date: July, 1988 :Date: July, 1988
This is a copy of UCI ML Wine recognition datasets. This is a copy of UCI ML Wine recognition datasets.
https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data
The data is the results of a chemical analysis of wines grown in the same The data is the results of a chemical analysis of wines grown in the same
region in Italy by three different cultivators. There are thirteen different region in Italy by three different cultivators. There are thirteen different
measurements taken for different constituents found in the three types of measurements taken for different constituents found in the three types of
wine. wine.
Original Owners: Original Owners:
Forina, M. et al, PARVUS - Forina, M. et al, PARVUS -
An Extendible Package for Data Exploration, Classification and Correlation. An Extendible Package for Data Exploration, Classification and Correlation.
Institute of Pharmaceutical and Food Analysis and Technologies, Institute of Pharmaceutical and Food Analysis and Technologies,
Via Brigata Salerno, 16147 Genoa, Italy. Via Brigata Salerno, 16147 Genoa, Italy.
Citation: Citation:
Lichman, M. (2013). UCI Machine Learning Repository Lichman, M. (2013). UCI Machine Learning Repository
[https://archive.ics.uci.edu/ml]. Irvine, CA: University of California, [https://archive.ics.uci.edu/ml]. Irvine, CA: University of California,
School of Information and Computer Science. School of Information and Computer Science.
.. dropdown:: References .. dropdown:: References
(1) S. Aeberhard, D. Coomans and O. de Vel, (1) S. Aeberhard, D. Coomans and O. de Vel,
Comparison of Classifiers in High Dimensional Settings, Comparison of Classifiers in High Dimensional Settings,
Tech. Rep. no. 92-02, (1992), Dept. of Computer Science and Dept. of Tech. Rep. no. 92-02, (1992), Dept. of Computer Science and Dept. of
Mathematics and Statistics, James Cook University of North Queensland. Mathematics and Statistics, James Cook University of North Queensland.
(Also submitted to Technometrics). (Also submitted to Technometrics).
The data was used with many others for comparing various The data was used with many others for comparing various
classifiers. The classes are separable, though only RDA classifiers. The classes are separable, though only RDA
has achieved 100% correct classification. has achieved 100% correct classification.
(RDA : 100%, QDA 99.4%, LDA 98.9%, 1NN 96.1% (z-transformed data)) (RDA : 100%, QDA 99.4%, LDA 98.9%, 1NN 96.1% (z-transformed data))
(All results using the leave-one-out technique) (All results using the leave-one-out technique)
(2) S. Aeberhard, D. Coomans and O. de Vel, (2) S. Aeberhard, D. Coomans and O. de Vel,
"THE CLASSIFICATION PERFORMANCE OF RDA" "THE CLASSIFICATION PERFORMANCE OF RDA"
Tech. Rep. no. 92-01, (1992), Dept. of Computer Science and Dept. of Tech. Rep. no. 92-01, (1992), Dept. of Computer Science and Dept. of
Mathematics and Statistics, James Cook University of North Queensland. Mathematics and Statistics, James Cook University of North Queensland.
(Also submitted to Journal of Chemometrics). (Also submitted to Journal of Chemometrics).
@@ -1,18 +1,18 @@
Image: china.jpg Image: china.jpg
Released under a creative commons license. [1] Released under a creative commons license. [1]
Attribution: Some rights reserved by danielbuechele [2] Attribution: Some rights reserved by danielbuechele [2]
Retrieved 21st August, 2011 from [3] by Robert Layton Retrieved 21st August, 2011 from [3] by Robert Layton
[1] https://creativecommons.org/licenses/by/2.0/ [1] https://creativecommons.org/licenses/by/2.0/
[2] https://www.flickr.com/photos/danielbuechele/ [2] https://www.flickr.com/photos/danielbuechele/
[3] https://www.flickr.com/photos/danielbuechele/6061409035/sizes/z/in/photostream/ [3] https://www.flickr.com/photos/danielbuechele/6061409035/sizes/z/in/photostream/
Image: flower.jpg Image: flower.jpg
Released under a creative commons license. [1] Released under a creative commons license. [1]
Attribution: Some rights reserved by danielbuechele [2] Attribution: Some rights reserved by danielbuechele [2]
Retrieved 21st August, 2011 from [3] by Robert Layton Retrieved 21st August, 2011 from [3] by Robert Layton
[1] https://creativecommons.org/licenses/by/2.0/ [1] https://creativecommons.org/licenses/by/2.0/
[2] https://www.flickr.com/photos/vultilion/ [2] https://www.flickr.com/photos/vultilion/
[3] https://www.flickr.com/photos/vultilion/6056698931/sizes/z/in/photostream/ [3] https://www.flickr.com/photos/vultilion/6056698931/sizes/z/in/photostream/
@@ -1,7 +1,7 @@
py.extension_module( py.extension_module(
'_svmlight_format_fast', '_svmlight_format_fast',
cython_gen.process('_svmlight_format_fast.pyx'), cython_gen.process('_svmlight_format_fast.pyx'),
dependencies: [np_dep], dependencies: [np_dep],
subdir: 'sklearn/datasets', subdir: 'sklearn/datasets',
install: true install: true
) )
@@ -1,9 +1,9 @@
# comment # comment
# note: the next line contains a tab # note: the next line contains a tab
1.0 3:2.5 11:-5.2 16:1.5 # and an inline comment 1.0 3:2.5 11:-5.2 16:1.5 # and an inline comment
2.0 6:1.0 13:-3 2.0 6:1.0 13:-3
# another comment # another comment
3.0 21:27 3.0 21:27
4.0 2:1.234567890123456e10 # double precision value 4.0 2:1.234567890123456e10 # double precision value
1.0 # empty line, all zeros 1.0 # empty line, all zeros
2.0 3:0 # explicit zeros 2.0 3:0 # explicit zeros
@@ -1,3 +1,3 @@
python 2:2.5 10:-5.2 15:1.5 python 2:2.5 10:-5.2 15:1.5
2.0 5:1.0 12:-3 2.0 5:1.0 12:-3
3.0 20:27 3.0 20:27
@@ -1 +1 @@
-1 5:2.5 2:-5.2 15:1.5 -1 5:2.5 2:-5.2 15:1.5
@@ -1,5 +1,5 @@
# multilabel dataset in SVMlight format # multilabel dataset in SVMlight format
1,0 2:2.5 10:-5.2 15:1.5 1,0 2:2.5 10:-5.2 15:1.5
2 5:1.0 12:-3 2 5:1.0 12:-3
2:3.5 11:26 2:3.5 11:26
1,2 20:27 1,2 20:27
@@ -1,38 +1,38 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from cython cimport floating from cython cimport floating
from libc.math cimport fabs from libc.math cimport fabs
def _update_cdnmf_fast(floating[:, ::1] W, floating[:, :] HHt, def _update_cdnmf_fast(floating[:, ::1] W, floating[:, :] HHt,
floating[:, :] XHt, Py_ssize_t[::1] permutation): floating[:, :] XHt, Py_ssize_t[::1] permutation):
cdef: cdef:
floating violation = 0 floating violation = 0
Py_ssize_t n_components = W.shape[1] Py_ssize_t n_components = W.shape[1]
Py_ssize_t n_samples = W.shape[0] # n_features for H update Py_ssize_t n_samples = W.shape[0] # n_features for H update
floating grad, pg, hess floating grad, pg, hess
Py_ssize_t i, r, s, t Py_ssize_t i, r, s, t
with nogil: with nogil:
for s in range(n_components): for s in range(n_components):
t = permutation[s] t = permutation[s]
for i in range(n_samples): for i in range(n_samples):
# gradient = GW[t, i] where GW = np.dot(W, HHt) - XHt # gradient = GW[t, i] where GW = np.dot(W, HHt) - XHt
grad = -XHt[i, t] grad = -XHt[i, t]
for r in range(n_components): for r in range(n_components):
grad += HHt[t, r] * W[i, r] grad += HHt[t, r] * W[i, r]
# projected gradient # projected gradient
pg = min(0., grad) if W[i, t] == 0 else grad pg = min(0., grad) if W[i, t] == 0 else grad
violation += fabs(pg) violation += fabs(pg)
# Hessian # Hessian
hess = HHt[t, t] hess = HHt[t, t]
if hess != 0: if hess != 0:
W[i, t] = max(W[i, t] - grad / hess, 0.) W[i, t] = max(W[i, t] - grad / hess, 0.)
return violation return violation
@@ -1,110 +1,110 @@
import numpy as np import numpy as np
from cython cimport floating from cython cimport floating
from libc.math cimport exp, fabs, log from libc.math cimport exp, fabs, log
from ..utils._typedefs cimport float64_t, intp_t from ..utils._typedefs cimport float64_t, intp_t
def mean_change(const floating[:] arr_1, const floating[:] arr_2): def mean_change(const floating[:] arr_1, const floating[:] arr_2):
"""Calculate the mean difference between two arrays. """Calculate the mean difference between two arrays.
Equivalent to np.abs(arr_1 - arr2).mean(). Equivalent to np.abs(arr_1 - arr2).mean().
""" """
cdef float64_t total, diff cdef float64_t total, diff
cdef intp_t i, size cdef intp_t i, size
size = arr_1.shape[0] size = arr_1.shape[0]
total = 0.0 total = 0.0
for i in range(size): for i in range(size):
diff = fabs(arr_1[i] - arr_2[i]) diff = fabs(arr_1[i] - arr_2[i])
total += diff total += diff
return total / size return total / size
def _dirichlet_expectation_1d( def _dirichlet_expectation_1d(
floating[:] doc_topic, floating[:] doc_topic,
floating doc_topic_prior, floating doc_topic_prior,
floating[:] out floating[:] out
): ):
"""Dirichlet expectation for a single sample: """Dirichlet expectation for a single sample:
exp(E[log(theta)]) for theta ~ Dir(doc_topic) exp(E[log(theta)]) for theta ~ Dir(doc_topic)
after adding doc_topic_prior to doc_topic, in-place. after adding doc_topic_prior to doc_topic, in-place.
Equivalent to Equivalent to
doc_topic += doc_topic_prior doc_topic += doc_topic_prior
out[:] = np.exp(psi(doc_topic) - psi(np.sum(doc_topic))) out[:] = np.exp(psi(doc_topic) - psi(np.sum(doc_topic)))
""" """
cdef floating dt, psi_total, total cdef floating dt, psi_total, total
cdef intp_t i, size cdef intp_t i, size
size = doc_topic.shape[0] size = doc_topic.shape[0]
total = 0.0 total = 0.0
for i in range(size): for i in range(size):
dt = doc_topic[i] + doc_topic_prior dt = doc_topic[i] + doc_topic_prior
doc_topic[i] = dt doc_topic[i] = dt
total += dt total += dt
psi_total = psi(total) psi_total = psi(total)
for i in range(size): for i in range(size):
out[i] = exp(psi(doc_topic[i]) - psi_total) out[i] = exp(psi(doc_topic[i]) - psi_total)
def _dirichlet_expectation_2d(const floating[:, :] arr): def _dirichlet_expectation_2d(const floating[:, :] arr):
"""Dirichlet expectation for multiple samples: """Dirichlet expectation for multiple samples:
E[log(theta)] for theta ~ Dir(arr). E[log(theta)] for theta ~ Dir(arr).
Equivalent to psi(arr) - psi(np.sum(arr, axis=1))[:, np.newaxis]. Equivalent to psi(arr) - psi(np.sum(arr, axis=1))[:, np.newaxis].
Note that unlike _dirichlet_expectation_1d, this function doesn't compute Note that unlike _dirichlet_expectation_1d, this function doesn't compute
the exp and doesn't add in the prior. the exp and doesn't add in the prior.
""" """
cdef floating row_total, psi_row_total cdef floating row_total, psi_row_total
cdef floating[:, :] d_exp cdef floating[:, :] d_exp
cdef intp_t i, j, n_rows, n_cols cdef intp_t i, j, n_rows, n_cols
n_rows = arr.shape[0] n_rows = arr.shape[0]
n_cols = arr.shape[1] n_cols = arr.shape[1]
d_exp = np.empty_like(arr) d_exp = np.empty_like(arr)
for i in range(n_rows): for i in range(n_rows):
row_total = 0 row_total = 0
for j in range(n_cols): for j in range(n_cols):
row_total += arr[i, j] row_total += arr[i, j]
psi_row_total = psi(row_total) psi_row_total = psi(row_total)
for j in range(n_cols): for j in range(n_cols):
d_exp[i, j] = psi(arr[i, j]) - psi_row_total d_exp[i, j] = psi(arr[i, j]) - psi_row_total
return d_exp.base return d_exp.base
# Psi function for positive arguments. Optimized for speed, not accuracy. # Psi function for positive arguments. Optimized for speed, not accuracy.
# #
# After: J. Bernardo (1976). Algorithm AS 103: Psi (Digamma) Function. # After: J. Bernardo (1976). Algorithm AS 103: Psi (Digamma) Function.
# https://www.uv.es/~bernardo/1976AppStatist.pdf # https://www.uv.es/~bernardo/1976AppStatist.pdf
cdef floating psi(floating x) noexcept nogil: cdef floating psi(floating x) noexcept nogil:
cdef double EULER = 0.577215664901532860606512090082402431 cdef double EULER = 0.577215664901532860606512090082402431
if x <= 1e-6: if x <= 1e-6:
# psi(x) = -EULER - 1/x + O(x) # psi(x) = -EULER - 1/x + O(x)
return -EULER - 1. / x return -EULER - 1. / x
cdef floating r, result = 0 cdef floating r, result = 0
# psi(x + 1) = psi(x) + 1/x # psi(x + 1) = psi(x) + 1/x
while x < 6: while x < 6:
result -= 1. / x result -= 1. / x
x += 1 x += 1
# psi(x) = log(x) - 1/(2x) - 1/(12x**2) + 1/(120x**4) - 1/(252x**6) # psi(x) = log(x) - 1/(2x) - 1/(12x**2) + 1/(120x**4) - 1/(252x**6)
# + O(1/x**8) # + O(1/x**8)
r = 1. / x r = 1. / x
result += log(x) - .5 * r result += log(x) - .5 * r
r = r * r r = r * r
result -= r * ((1./12.) - r * ((1./120.) - r * (1./252.))) result -= r * ((1./12.) - r * ((1./120.) - r * (1./252.)))
return result return result
@@ -1,14 +1,14 @@
py.extension_module( py.extension_module(
'_online_lda_fast', '_online_lda_fast',
[cython_gen.process('_online_lda_fast.pyx'), utils_cython_tree], [cython_gen.process('_online_lda_fast.pyx'), utils_cython_tree],
subdir: 'sklearn/decomposition', subdir: 'sklearn/decomposition',
install: true install: true
) )
py.extension_module( py.extension_module(
'_cdnmf_fast', '_cdnmf_fast',
cython_gen.process('_cdnmf_fast.pyx'), cython_gen.process('_cdnmf_fast.pyx'),
dependencies: [np_dep], dependencies: [np_dep],
subdir: 'sklearn/decomposition', subdir: 'sklearn/decomposition',
install: true install: true
) )
@@ -1,262 +1,262 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from libc.stdlib cimport free from libc.stdlib cimport free
from libc.string cimport memset from libc.string cimport memset
import numpy as np import numpy as np
from scipy.sparse import issparse from scipy.sparse import issparse
from ..utils._typedefs cimport float32_t, float64_t, intp_t, int32_t, uint8_t from ..utils._typedefs cimport float32_t, float64_t, intp_t, int32_t, uint8_t
# Note: _tree uses cimport numpy, cnp.import_array, so we need to include # Note: _tree uses cimport numpy, cnp.import_array, so we need to include
# numpy headers in the build configuration of this extension # numpy headers in the build configuration of this extension
from ..tree._tree cimport Node from ..tree._tree cimport Node
from ..tree._tree cimport Tree from ..tree._tree cimport Tree
from ..tree._utils cimport safe_realloc from ..tree._utils cimport safe_realloc
# no namespace lookup for numpy dtype and array creation # no namespace lookup for numpy dtype and array creation
from numpy import zeros as np_zeros from numpy import zeros as np_zeros
# constant to mark tree leafs # constant to mark tree leafs
cdef intp_t TREE_LEAF = -1 cdef intp_t TREE_LEAF = -1
cdef void _predict_regression_tree_inplace_fast_dense( cdef void _predict_regression_tree_inplace_fast_dense(
const float32_t[:, ::1] X, const float32_t[:, ::1] X,
Node* root_node, Node* root_node,
double *value, double *value,
double scale, double scale,
Py_ssize_t k, Py_ssize_t k,
float64_t[:, :] out float64_t[:, :] out
) noexcept nogil: ) noexcept nogil:
"""Predicts output for regression tree and stores it in ``out[i, k]``. """Predicts output for regression tree and stores it in ``out[i, k]``.
This function operates directly on the data arrays of the tree This function operates directly on the data arrays of the tree
data structures. This is 5x faster than the variant above because data structures. This is 5x faster than the variant above because
it allows us to avoid buffer validation. it allows us to avoid buffer validation.
The function assumes that the ndarray that wraps ``X`` is The function assumes that the ndarray that wraps ``X`` is
c-continuous. c-continuous.
Parameters Parameters
---------- ----------
X : float32_t 2d memory view X : float32_t 2d memory view
The memory view on the data ndarray of the input ``X``. The memory view on the data ndarray of the input ``X``.
Assumes that the array is c-continuous. Assumes that the array is c-continuous.
root_node : tree Node pointer root_node : tree Node pointer
Pointer to the main node array of the :class:``sklearn.tree.Tree``. Pointer to the main node array of the :class:``sklearn.tree.Tree``.
value : np.float64_t pointer value : np.float64_t pointer
The pointer to the data array of the ``value`` array attribute The pointer to the data array of the ``value`` array attribute
of the :class:``sklearn.tree.Tree``. of the :class:``sklearn.tree.Tree``.
scale : double scale : double
A constant to scale the predictions. A constant to scale the predictions.
k : int k : int
The index of the tree output to be predicted. Must satisfy The index of the tree output to be predicted. Must satisfy
0 <= ``k`` < ``K``. 0 <= ``k`` < ``K``.
out : memory view on array of type np.float64_t out : memory view on array of type np.float64_t
The data array where the predictions are stored. The data array where the predictions are stored.
``out`` is assumed to be a two-dimensional array of ``out`` is assumed to be a two-dimensional array of
shape ``(n_samples, K)``. shape ``(n_samples, K)``.
""" """
cdef intp_t n_samples = X.shape[0] cdef intp_t n_samples = X.shape[0]
cdef Py_ssize_t i cdef Py_ssize_t i
cdef Node *node cdef Node *node
for i in range(n_samples): for i in range(n_samples):
node = root_node node = root_node
# While node not a leaf # While node not a leaf
while node.left_child != TREE_LEAF: while node.left_child != TREE_LEAF:
if X[i, node.feature] <= node.threshold: if X[i, node.feature] <= node.threshold:
node = root_node + node.left_child node = root_node + node.left_child
else: else:
node = root_node + node.right_child node = root_node + node.right_child
out[i, k] += scale * value[node - root_node] out[i, k] += scale * value[node - root_node]
def _predict_regression_tree_stages_sparse( def _predict_regression_tree_stages_sparse(
object[:, :] estimators, object[:, :] estimators,
object X, object X,
double scale, double scale,
float64_t[:, :] out float64_t[:, :] out
): ):
"""Predicts output for regression tree inplace and adds scaled value to ``out[i, k]``. """Predicts output for regression tree inplace and adds scaled value to ``out[i, k]``.
The function assumes that the ndarray that wraps ``X`` is csr_matrix. The function assumes that the ndarray that wraps ``X`` is csr_matrix.
""" """
cdef const float32_t[::1] X_data = X.data cdef const float32_t[::1] X_data = X.data
cdef const int32_t[::1] X_indices = X.indices cdef const int32_t[::1] X_indices = X.indices
cdef const int32_t[::1] X_indptr = X.indptr cdef const int32_t[::1] X_indptr = X.indptr
cdef intp_t n_samples = X.shape[0] cdef intp_t n_samples = X.shape[0]
cdef intp_t n_features = X.shape[1] cdef intp_t n_features = X.shape[1]
cdef intp_t n_stages = estimators.shape[0] cdef intp_t n_stages = estimators.shape[0]
cdef intp_t n_outputs = estimators.shape[1] cdef intp_t n_outputs = estimators.shape[1]
# Indices and temporary variables # Indices and temporary variables
cdef intp_t sample_i cdef intp_t sample_i
cdef intp_t feature_i cdef intp_t feature_i
cdef intp_t stage_i cdef intp_t stage_i
cdef intp_t output_i cdef intp_t output_i
cdef Node *root_node = NULL cdef Node *root_node = NULL
cdef Node *node = NULL cdef Node *node = NULL
cdef double *value = NULL cdef double *value = NULL
cdef Tree tree cdef Tree tree
cdef Node** nodes = NULL cdef Node** nodes = NULL
cdef double** values = NULL cdef double** values = NULL
safe_realloc(&nodes, n_stages * n_outputs) safe_realloc(&nodes, n_stages * n_outputs)
safe_realloc(&values, n_stages * n_outputs) safe_realloc(&values, n_stages * n_outputs)
for stage_i in range(n_stages): for stage_i in range(n_stages):
for output_i in range(n_outputs): for output_i in range(n_outputs):
tree = estimators[stage_i, output_i].tree_ tree = estimators[stage_i, output_i].tree_
nodes[stage_i * n_outputs + output_i] = tree.nodes nodes[stage_i * n_outputs + output_i] = tree.nodes
values[stage_i * n_outputs + output_i] = tree.value values[stage_i * n_outputs + output_i] = tree.value
# Initialize auxiliary data-structure # Initialize auxiliary data-structure
cdef float32_t feature_value = 0. cdef float32_t feature_value = 0.
cdef float32_t* X_sample = NULL cdef float32_t* X_sample = NULL
# feature_to_sample as a data structure records the last seen sample # feature_to_sample as a data structure records the last seen sample
# for each feature; functionally, it is an efficient way to identify # for each feature; functionally, it is an efficient way to identify
# which features are nonzero in the present sample. # which features are nonzero in the present sample.
cdef intp_t* feature_to_sample = NULL cdef intp_t* feature_to_sample = NULL
safe_realloc(&X_sample, n_features) safe_realloc(&X_sample, n_features)
safe_realloc(&feature_to_sample, n_features) safe_realloc(&feature_to_sample, n_features)
memset(feature_to_sample, -1, n_features * sizeof(intp_t)) memset(feature_to_sample, -1, n_features * sizeof(intp_t))
# Cycle through all samples # Cycle through all samples
for sample_i in range(n_samples): for sample_i in range(n_samples):
for feature_i in range(X_indptr[sample_i], X_indptr[sample_i + 1]): for feature_i in range(X_indptr[sample_i], X_indptr[sample_i + 1]):
feature_to_sample[X_indices[feature_i]] = sample_i feature_to_sample[X_indices[feature_i]] = sample_i
X_sample[X_indices[feature_i]] = X_data[feature_i] X_sample[X_indices[feature_i]] = X_data[feature_i]
# Cycle through all stages # Cycle through all stages
for stage_i in range(n_stages): for stage_i in range(n_stages):
# Cycle through all trees # Cycle through all trees
for output_i in range(n_outputs): for output_i in range(n_outputs):
root_node = nodes[stage_i * n_outputs + output_i] root_node = nodes[stage_i * n_outputs + output_i]
value = values[stage_i * n_outputs + output_i] value = values[stage_i * n_outputs + output_i]
node = root_node node = root_node
# While node not a leaf # While node not a leaf
while node.left_child != TREE_LEAF: while node.left_child != TREE_LEAF:
# ... and node.right_child != TREE_LEAF: # ... and node.right_child != TREE_LEAF:
if feature_to_sample[node.feature] == sample_i: if feature_to_sample[node.feature] == sample_i:
feature_value = X_sample[node.feature] feature_value = X_sample[node.feature]
else: else:
feature_value = 0. feature_value = 0.
if feature_value <= node.threshold: if feature_value <= node.threshold:
node = root_node + node.left_child node = root_node + node.left_child
else: else:
node = root_node + node.right_child node = root_node + node.right_child
out[sample_i, output_i] += scale * value[node - root_node] out[sample_i, output_i] += scale * value[node - root_node]
# Free auxiliary arrays # Free auxiliary arrays
free(X_sample) free(X_sample)
free(feature_to_sample) free(feature_to_sample)
free(nodes) free(nodes)
free(values) free(values)
def predict_stages( def predict_stages(
object[:, :] estimators, object[:, :] estimators,
object X, object X,
double scale, double scale,
float64_t[:, :] out float64_t[:, :] out
): ):
"""Add predictions of ``estimators`` to ``out``. """Add predictions of ``estimators`` to ``out``.
Each estimator is scaled by ``scale`` before its prediction Each estimator is scaled by ``scale`` before its prediction
is added to ``out``. is added to ``out``.
""" """
cdef Py_ssize_t i cdef Py_ssize_t i
cdef Py_ssize_t k cdef Py_ssize_t k
cdef Py_ssize_t n_estimators = estimators.shape[0] cdef Py_ssize_t n_estimators = estimators.shape[0]
cdef Py_ssize_t K = estimators.shape[1] cdef Py_ssize_t K = estimators.shape[1]
cdef Tree tree cdef Tree tree
if issparse(X): if issparse(X):
if X.format != 'csr': if X.format != 'csr':
raise ValueError("When X is a sparse matrix, a CSR format is" raise ValueError("When X is a sparse matrix, a CSR format is"
" expected, got {!r}".format(type(X))) " expected, got {!r}".format(type(X)))
_predict_regression_tree_stages_sparse( _predict_regression_tree_stages_sparse(
estimators=estimators, X=X, scale=scale, out=out estimators=estimators, X=X, scale=scale, out=out
) )
else: else:
if not isinstance(X, np.ndarray) or np.isfortran(X): if not isinstance(X, np.ndarray) or np.isfortran(X):
raise ValueError(f"X should be C-ordered np.ndarray, got {type(X)}") raise ValueError(f"X should be C-ordered np.ndarray, got {type(X)}")
for i in range(n_estimators): for i in range(n_estimators):
for k in range(K): for k in range(K):
tree = estimators[i, k].tree_ tree = estimators[i, k].tree_
# avoid buffer validation by casting to ndarray # avoid buffer validation by casting to ndarray
# and get data pointer # and get data pointer
# need brackets because of casting operator priority # need brackets because of casting operator priority
_predict_regression_tree_inplace_fast_dense( _predict_regression_tree_inplace_fast_dense(
X=X, X=X,
root_node=tree.nodes, root_node=tree.nodes,
value=tree.value, value=tree.value,
scale=scale, scale=scale,
k=k, k=k,
out=out out=out
) )
# out[:, k] += scale * tree.predict(X).ravel() # out[:, k] += scale * tree.predict(X).ravel()
def predict_stage( def predict_stage(
object[:, :] estimators, object[:, :] estimators,
int stage, int stage,
object X, object X,
double scale, double scale,
float64_t[:, :] out float64_t[:, :] out
): ):
"""Add predictions of ``estimators[stage]`` to ``out``. """Add predictions of ``estimators[stage]`` to ``out``.
Each estimator in the stage is scaled by ``scale`` before Each estimator in the stage is scaled by ``scale`` before
its prediction is added to ``out``. its prediction is added to ``out``.
""" """
return predict_stages( return predict_stages(
estimators=estimators[stage:stage + 1], X=X, scale=scale, out=out estimators=estimators[stage:stage + 1], X=X, scale=scale, out=out
) )
def _random_sample_mask( def _random_sample_mask(
intp_t n_total_samples, intp_t n_total_samples,
intp_t n_total_in_bag, intp_t n_total_in_bag,
random_state random_state
): ):
"""Create a random sample mask where ``n_total_in_bag`` elements are set. """Create a random sample mask where ``n_total_in_bag`` elements are set.
Parameters Parameters
---------- ----------
n_total_samples : int n_total_samples : int
The length of the resulting mask. The length of the resulting mask.
n_total_in_bag : int n_total_in_bag : int
The number of elements in the sample mask which are set to 1. The number of elements in the sample mask which are set to 1.
random_state : RandomState random_state : RandomState
A numpy ``RandomState`` object. A numpy ``RandomState`` object.
Returns Returns
------- -------
sample_mask : np.ndarray, shape=[n_total_samples] sample_mask : np.ndarray, shape=[n_total_samples]
An ndarray where ``n_total_in_bag`` elements are set to ``True`` An ndarray where ``n_total_in_bag`` elements are set to ``True``
the others are ``False``. the others are ``False``.
""" """
cdef float64_t[::1] rand = random_state.uniform(size=n_total_samples) cdef float64_t[::1] rand = random_state.uniform(size=n_total_samples)
cdef uint8_t[::1] sample_mask = np_zeros((n_total_samples,), dtype=bool) cdef uint8_t[::1] sample_mask = np_zeros((n_total_samples,), dtype=bool)
cdef intp_t n_bagged = 0 cdef intp_t n_bagged = 0
cdef intp_t i = 0 cdef intp_t i = 0
for i in range(n_total_samples): for i in range(n_total_samples):
if rand[i] * (n_total_samples - i) < (n_total_in_bag - n_bagged): if rand[i] * (n_total_samples - i) < (n_total_in_bag - n_bagged):
sample_mask[i] = 1 sample_mask[i] = 1
n_bagged += 1 n_bagged += 1
return sample_mask.base return sample_mask.base
@@ -1,85 +1,85 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from cython.parallel import prange from cython.parallel import prange
from libc.math cimport isnan from libc.math cimport isnan
from .common cimport X_DTYPE_C, X_BINNED_DTYPE_C from .common cimport X_DTYPE_C, X_BINNED_DTYPE_C
from ...utils._typedefs cimport uint8_t from ...utils._typedefs cimport uint8_t
def _map_to_bins(const X_DTYPE_C [:, :] data, def _map_to_bins(const X_DTYPE_C [:, :] data,
list binning_thresholds, list binning_thresholds,
const uint8_t[::1] is_categorical, const uint8_t[::1] is_categorical,
const uint8_t missing_values_bin_idx, const uint8_t missing_values_bin_idx,
int n_threads, int n_threads,
X_BINNED_DTYPE_C [::1, :] binned): X_BINNED_DTYPE_C [::1, :] binned):
"""Bin continuous and categorical values to discrete integer-coded levels. """Bin continuous and categorical values to discrete integer-coded levels.
A given value x is mapped into bin value i iff A given value x is mapped into bin value i iff
thresholds[i - 1] < x <= thresholds[i] thresholds[i - 1] < x <= thresholds[i]
Parameters Parameters
---------- ----------
data : ndarray, shape (n_samples, n_features) data : ndarray, shape (n_samples, n_features)
The data to bin. The data to bin.
binning_thresholds : list of arrays binning_thresholds : list of arrays
For each feature, stores the increasing numeric values that are For each feature, stores the increasing numeric values that are
used to separate the bins. used to separate the bins.
is_categorical : ndarray of uint8_t of shape (n_features,) is_categorical : ndarray of uint8_t of shape (n_features,)
Indicates categorical features. Indicates categorical features.
n_threads : int n_threads : int
Number of OpenMP threads to use. Number of OpenMP threads to use.
binned : ndarray, shape (n_samples, n_features) binned : ndarray, shape (n_samples, n_features)
Output array, must be fortran aligned. Output array, must be fortran aligned.
""" """
cdef: cdef:
int feature_idx int feature_idx
for feature_idx in range(data.shape[1]): for feature_idx in range(data.shape[1]):
_map_col_to_bins( _map_col_to_bins(
data[:, feature_idx], data[:, feature_idx],
binning_thresholds[feature_idx], binning_thresholds[feature_idx],
is_categorical[feature_idx], is_categorical[feature_idx],
missing_values_bin_idx, missing_values_bin_idx,
n_threads, n_threads,
binned[:, feature_idx] binned[:, feature_idx]
) )
cdef void _map_col_to_bins( cdef void _map_col_to_bins(
const X_DTYPE_C [:] data, const X_DTYPE_C [:] data,
const X_DTYPE_C [:] binning_thresholds, const X_DTYPE_C [:] binning_thresholds,
const uint8_t is_categorical, const uint8_t is_categorical,
const uint8_t missing_values_bin_idx, const uint8_t missing_values_bin_idx,
int n_threads, int n_threads,
X_BINNED_DTYPE_C [:] binned X_BINNED_DTYPE_C [:] binned
): ):
"""Binary search to find the bin index for each value in the data.""" """Binary search to find the bin index for each value in the data."""
cdef: cdef:
int i int i
int left int left
int right int right
int middle int middle
for i in prange(data.shape[0], schedule='static', nogil=True, for i in prange(data.shape[0], schedule='static', nogil=True,
num_threads=n_threads): num_threads=n_threads):
if ( if (
isnan(data[i]) or isnan(data[i]) or
# To follow LightGBM's conventions, negative values for # To follow LightGBM's conventions, negative values for
# categorical features are considered as missing values. # categorical features are considered as missing values.
(is_categorical and data[i] < 0) (is_categorical and data[i] < 0)
): ):
binned[i] = missing_values_bin_idx binned[i] = missing_values_bin_idx
else: else:
# for known values, use binary search # for known values, use binary search
left, right = 0, binning_thresholds.shape[0] left, right = 0, binning_thresholds.shape[0]
while left < right: while left < right:
# equal to (right + left - 1) // 2 but avoids overflow # equal to (right + left - 1) // 2 but avoids overflow
middle = left + (right - left - 1) // 2 middle = left + (right - left - 1) // 2
if data[i] <= binning_thresholds[middle]: if data[i] <= binning_thresholds[middle]:
right = middle right = middle
else: else:
left = middle + 1 left = middle + 1
binned[i] = left binned[i] = left
@@ -1,20 +1,20 @@
from .common cimport X_BINNED_DTYPE_C from .common cimport X_BINNED_DTYPE_C
from .common cimport BITSET_DTYPE_C from .common cimport BITSET_DTYPE_C
from .common cimport BITSET_INNER_DTYPE_C from .common cimport BITSET_INNER_DTYPE_C
from .common cimport X_DTYPE_C from .common cimport X_DTYPE_C
from ...utils._typedefs cimport uint8_t from ...utils._typedefs cimport uint8_t
cdef void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil cdef void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil
cdef void set_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil cdef void set_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
cdef uint8_t in_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil cdef uint8_t in_bitset(BITSET_DTYPE_C bitset, X_BINNED_DTYPE_C val) noexcept nogil
cpdef uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset, cpdef uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
X_BINNED_DTYPE_C val) noexcept nogil X_BINNED_DTYPE_C val) noexcept nogil
cdef uint8_t in_bitset_2d_memoryview( cdef uint8_t in_bitset_2d_memoryview(
const BITSET_INNER_DTYPE_C[:, :] bitset, const BITSET_INNER_DTYPE_C[:, :] bitset,
X_BINNED_DTYPE_C val, X_BINNED_DTYPE_C val,
unsigned int row) noexcept nogil unsigned int row) noexcept nogil
@@ -1,65 +1,65 @@
from .common cimport BITSET_INNER_DTYPE_C from .common cimport BITSET_INNER_DTYPE_C
from .common cimport BITSET_DTYPE_C from .common cimport BITSET_DTYPE_C
from .common cimport X_DTYPE_C from .common cimport X_DTYPE_C
from .common cimport X_BINNED_DTYPE_C from .common cimport X_BINNED_DTYPE_C
from ...utils._typedefs cimport uint8_t from ...utils._typedefs cimport uint8_t
# A bitset is a data structure used to represent sets of integers in [0, n]. We # A bitset is a data structure used to represent sets of integers in [0, n]. We
# use them to represent sets of features indices (e.g. features that go to the # use them to represent sets of features indices (e.g. features that go to the
# left child, or features that are categorical). For familiarity with bitsets # left child, or features that are categorical). For familiarity with bitsets
# and bitwise operations: # and bitwise operations:
# https://en.wikipedia.org/wiki/Bit_array # https://en.wikipedia.org/wiki/Bit_array
# https://en.wikipedia.org/wiki/Bitwise_operation # https://en.wikipedia.org/wiki/Bitwise_operation
cdef inline void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil: # OUT cdef inline void init_bitset(BITSET_DTYPE_C bitset) noexcept nogil: # OUT
cdef: cdef:
unsigned int i unsigned int i
for i in range(8): for i in range(8):
bitset[i] = 0 bitset[i] = 0
cdef inline void set_bitset(BITSET_DTYPE_C bitset, # OUT cdef inline void set_bitset(BITSET_DTYPE_C bitset, # OUT
X_BINNED_DTYPE_C val) noexcept nogil: X_BINNED_DTYPE_C val) noexcept nogil:
bitset[val // 32] |= (1 << (val % 32)) bitset[val // 32] |= (1 << (val % 32))
cdef inline uint8_t in_bitset(BITSET_DTYPE_C bitset, cdef inline uint8_t in_bitset(BITSET_DTYPE_C bitset,
X_BINNED_DTYPE_C val) noexcept nogil: X_BINNED_DTYPE_C val) noexcept nogil:
return (bitset[val // 32] >> (val % 32)) & 1 return (bitset[val // 32] >> (val % 32)) & 1
cpdef inline uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset, cpdef inline uint8_t in_bitset_memoryview(const BITSET_INNER_DTYPE_C[:] bitset,
X_BINNED_DTYPE_C val) noexcept nogil: X_BINNED_DTYPE_C val) noexcept nogil:
return (bitset[val // 32] >> (val % 32)) & 1 return (bitset[val // 32] >> (val % 32)) & 1
cdef inline uint8_t in_bitset_2d_memoryview(const BITSET_INNER_DTYPE_C[:, :] bitset, cdef inline uint8_t in_bitset_2d_memoryview(const BITSET_INNER_DTYPE_C[:, :] bitset,
X_BINNED_DTYPE_C val, X_BINNED_DTYPE_C val,
unsigned int row) noexcept nogil: unsigned int row) noexcept nogil:
# Same as above but works on 2d memory views to avoid the creation of 1d # Same as above but works on 2d memory views to avoid the creation of 1d
# memory views. See https://github.com/scikit-learn/scikit-learn/issues/17299 # memory views. See https://github.com/scikit-learn/scikit-learn/issues/17299
return (bitset[row, val // 32] >> (val % 32)) & 1 return (bitset[row, val // 32] >> (val % 32)) & 1
cpdef inline void set_bitset_memoryview(BITSET_INNER_DTYPE_C[:] bitset, # OUT cpdef inline void set_bitset_memoryview(BITSET_INNER_DTYPE_C[:] bitset, # OUT
X_BINNED_DTYPE_C val): X_BINNED_DTYPE_C val):
bitset[val // 32] |= (1 << (val % 32)) bitset[val // 32] |= (1 << (val % 32))
def set_raw_bitset_from_binned_bitset(BITSET_INNER_DTYPE_C[:] raw_bitset, # OUT def set_raw_bitset_from_binned_bitset(BITSET_INNER_DTYPE_C[:] raw_bitset, # OUT
BITSET_INNER_DTYPE_C[:] binned_bitset, BITSET_INNER_DTYPE_C[:] binned_bitset,
X_DTYPE_C[:] categories): X_DTYPE_C[:] categories):
"""Set the raw_bitset from the values of the binned bitset """Set the raw_bitset from the values of the binned bitset
categories is a mapping from binned category value to raw category value. categories is a mapping from binned category value to raw category value.
""" """
cdef: cdef:
int binned_cat_value int binned_cat_value
X_DTYPE_C raw_cat_value X_DTYPE_C raw_cat_value
for binned_cat_value, raw_cat_value in enumerate(categories): for binned_cat_value, raw_cat_value in enumerate(categories):
if in_bitset_memoryview(binned_bitset, binned_cat_value): if in_bitset_memoryview(binned_bitset, binned_cat_value):
set_bitset_memoryview(raw_bitset, <X_BINNED_DTYPE_C>raw_cat_value) set_bitset_memoryview(raw_bitset, <X_BINNED_DTYPE_C>raw_cat_value)
@@ -1,59 +1,59 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from cython.parallel import prange from cython.parallel import prange
import numpy as np import numpy as np
from .common import Y_DTYPE from .common import Y_DTYPE
from .common cimport Y_DTYPE_C from .common cimport Y_DTYPE_C
def _update_raw_predictions( def _update_raw_predictions(
Y_DTYPE_C [::1] raw_predictions, # OUT Y_DTYPE_C [::1] raw_predictions, # OUT
grower, grower,
n_threads, n_threads,
): ):
"""Update raw_predictions with the predictions of the newest tree. """Update raw_predictions with the predictions of the newest tree.
This is equivalent to (and much faster than): This is equivalent to (and much faster than):
raw_predictions += last_estimator.predict(X_train) raw_predictions += last_estimator.predict(X_train)
It's only possible for data X_train that is used to train the trees (it It's only possible for data X_train that is used to train the trees (it
isn't usable for e.g. X_val). isn't usable for e.g. X_val).
""" """
cdef: cdef:
unsigned int [::1] starts # start of each leaf in partition unsigned int [::1] starts # start of each leaf in partition
unsigned int [::1] stops # end of each leaf in partition unsigned int [::1] stops # end of each leaf in partition
Y_DTYPE_C [::1] values # value of each leaf Y_DTYPE_C [::1] values # value of each leaf
const unsigned int [::1] partition = grower.splitter.partition const unsigned int [::1] partition = grower.splitter.partition
list leaves list leaves
leaves = grower.finalized_leaves leaves = grower.finalized_leaves
starts = np.array([leaf.partition_start for leaf in leaves], starts = np.array([leaf.partition_start for leaf in leaves],
dtype=np.uint32) dtype=np.uint32)
stops = np.array([leaf.partition_stop for leaf in leaves], stops = np.array([leaf.partition_stop for leaf in leaves],
dtype=np.uint32) dtype=np.uint32)
values = np.array([leaf.value for leaf in leaves], dtype=Y_DTYPE) values = np.array([leaf.value for leaf in leaves], dtype=Y_DTYPE)
_update_raw_predictions_helper(raw_predictions, starts, stops, partition, _update_raw_predictions_helper(raw_predictions, starts, stops, partition,
values, n_threads) values, n_threads)
cdef inline void _update_raw_predictions_helper( cdef inline void _update_raw_predictions_helper(
Y_DTYPE_C [::1] raw_predictions, # OUT Y_DTYPE_C [::1] raw_predictions, # OUT
const unsigned int [::1] starts, const unsigned int [::1] starts,
const unsigned int [::1] stops, const unsigned int [::1] stops,
const unsigned int [::1] partition, const unsigned int [::1] partition,
const Y_DTYPE_C [::1] values, const Y_DTYPE_C [::1] values,
int n_threads, int n_threads,
): ):
cdef: cdef:
unsigned int position unsigned int position
int leaf_idx int leaf_idx
int n_leaves = starts.shape[0] int n_leaves = starts.shape[0]
for leaf_idx in prange(n_leaves, schedule='static', nogil=True, for leaf_idx in prange(n_leaves, schedule='static', nogil=True,
num_threads=n_threads): num_threads=n_threads):
for position in range(starts[leaf_idx], stops[leaf_idx]): for position in range(starts[leaf_idx], stops[leaf_idx]):
raw_predictions[partition[position]] += values[leaf_idx] raw_predictions[partition[position]] += values[leaf_idx]
@@ -1,256 +1,256 @@
# Authors: The scikit-learn developers # Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
from cython.parallel import prange from cython.parallel import prange
from libc.math cimport isnan from libc.math cimport isnan
import numpy as np import numpy as np
from ...utils._typedefs cimport intp_t, uint8_t from ...utils._typedefs cimport intp_t, uint8_t
from .common cimport X_DTYPE_C from .common cimport X_DTYPE_C
from .common cimport Y_DTYPE_C from .common cimport Y_DTYPE_C
from .common import Y_DTYPE from .common import Y_DTYPE
from .common cimport X_BINNED_DTYPE_C from .common cimport X_BINNED_DTYPE_C
from .common cimport BITSET_INNER_DTYPE_C from .common cimport BITSET_INNER_DTYPE_C
from .common cimport node_struct from .common cimport node_struct
from ._bitset cimport in_bitset_2d_memoryview from ._bitset cimport in_bitset_2d_memoryview
def _predict_from_raw_data( # raw data = non-binned data def _predict_from_raw_data( # raw data = non-binned data
const node_struct [:] nodes, const node_struct [:] nodes,
const X_DTYPE_C [:, :] numeric_data, const X_DTYPE_C [:, :] numeric_data,
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets, const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets, const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
const unsigned int [::1] f_idx_map, const unsigned int [::1] f_idx_map,
int n_threads, int n_threads,
Y_DTYPE_C [:] out): Y_DTYPE_C [:] out):
cdef: cdef:
int i int i
for i in prange(numeric_data.shape[0], schedule='static', nogil=True, for i in prange(numeric_data.shape[0], schedule='static', nogil=True,
num_threads=n_threads): num_threads=n_threads):
out[i] = _predict_one_from_raw_data( out[i] = _predict_one_from_raw_data(
nodes, numeric_data, raw_left_cat_bitsets, nodes, numeric_data, raw_left_cat_bitsets,
known_cat_bitsets, known_cat_bitsets,
f_idx_map, i) f_idx_map, i)
cdef inline Y_DTYPE_C _predict_one_from_raw_data( cdef inline Y_DTYPE_C _predict_one_from_raw_data(
const node_struct [:] nodes, const node_struct [:] nodes,
const X_DTYPE_C [:, :] numeric_data, const X_DTYPE_C [:, :] numeric_data,
const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets, const BITSET_INNER_DTYPE_C [:, ::1] raw_left_cat_bitsets,
const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets, const BITSET_INNER_DTYPE_C [:, ::1] known_cat_bitsets,
const unsigned int [::1] f_idx_map, const unsigned int [::1] f_idx_map,
const int row) noexcept nogil: const int row) noexcept nogil:
# Need to pass the whole array and the row index, else prange won't work. # Need to pass the whole array and the row index, else prange won't work.
# See issue Cython #2798 # See issue Cython #2798
cdef: cdef:
node_struct node = nodes[0] node_struct node = nodes[0]
unsigned int node_idx = 0 unsigned int node_idx = 0
X_DTYPE_C data_val X_DTYPE_C data_val
while True: while True:
if node.is_leaf: if node.is_leaf:
return node.value return node.value
data_val = numeric_data[row, node.feature_idx] data_val = numeric_data[row, node.feature_idx]
if isnan(data_val): if isnan(data_val):
if node.missing_go_to_left: if node.missing_go_to_left:
node_idx = node.left node_idx = node.left
else: else:
node_idx = node.right node_idx = node.right
elif node.is_categorical: elif node.is_categorical:
if data_val < 0: if data_val < 0:
# data_val is not in the accepted range, so it is treated as missing value # data_val is not in the accepted range, so it is treated as missing value
node_idx = node.left if node.missing_go_to_left else node.right node_idx = node.left if node.missing_go_to_left else node.right
elif in_bitset_2d_memoryview( elif in_bitset_2d_memoryview(
raw_left_cat_bitsets, raw_left_cat_bitsets,
<X_BINNED_DTYPE_C>data_val, <X_BINNED_DTYPE_C>data_val,
node.bitset_idx): node.bitset_idx):
node_idx = node.left node_idx = node.left
elif in_bitset_2d_memoryview( elif in_bitset_2d_memoryview(
known_cat_bitsets, known_cat_bitsets,
<X_BINNED_DTYPE_C>data_val, <X_BINNED_DTYPE_C>data_val,
f_idx_map[node.feature_idx]): f_idx_map[node.feature_idx]):
node_idx = node.right node_idx = node.right
else: else:
# Treat unknown categories as missing. # Treat unknown categories as missing.
node_idx = node.left if node.missing_go_to_left else node.right node_idx = node.left if node.missing_go_to_left else node.right
else: else:
if data_val <= node.num_threshold: if data_val <= node.num_threshold:
node_idx = node.left node_idx = node.left
else: else:
node_idx = node.right node_idx = node.right
node = nodes[node_idx] node = nodes[node_idx]
def _predict_from_binned_data( def _predict_from_binned_data(
node_struct [:] nodes, node_struct [:] nodes,
const X_BINNED_DTYPE_C [:, :] binned_data, const X_BINNED_DTYPE_C [:, :] binned_data,
BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets, BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
const uint8_t missing_values_bin_idx, const uint8_t missing_values_bin_idx,
int n_threads, int n_threads,
Y_DTYPE_C [:] out): Y_DTYPE_C [:] out):
cdef: cdef:
int i int i
for i in prange(binned_data.shape[0], schedule='static', nogil=True, for i in prange(binned_data.shape[0], schedule='static', nogil=True,
num_threads=n_threads): num_threads=n_threads):
out[i] = _predict_one_from_binned_data(nodes, out[i] = _predict_one_from_binned_data(nodes,
binned_data, binned_data,
binned_left_cat_bitsets, i, binned_left_cat_bitsets, i,
missing_values_bin_idx) missing_values_bin_idx)
cdef inline Y_DTYPE_C _predict_one_from_binned_data( cdef inline Y_DTYPE_C _predict_one_from_binned_data(
node_struct [:] nodes, node_struct [:] nodes,
const X_BINNED_DTYPE_C [:, :] binned_data, const X_BINNED_DTYPE_C [:, :] binned_data,
const BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets, const BITSET_INNER_DTYPE_C [:, :] binned_left_cat_bitsets,
const int row, const int row,
const uint8_t missing_values_bin_idx) noexcept nogil: const uint8_t missing_values_bin_idx) noexcept nogil:
# Need to pass the whole array and the row index, else prange won't work. # Need to pass the whole array and the row index, else prange won't work.
# See issue Cython #2798 # See issue Cython #2798
cdef: cdef:
node_struct node = nodes[0] node_struct node = nodes[0]
unsigned int node_idx = 0 unsigned int node_idx = 0
X_BINNED_DTYPE_C data_val X_BINNED_DTYPE_C data_val
while True: while True:
if node.is_leaf: if node.is_leaf:
return node.value return node.value
data_val = binned_data[row, node.feature_idx] data_val = binned_data[row, node.feature_idx]
if data_val == missing_values_bin_idx: if data_val == missing_values_bin_idx:
if node.missing_go_to_left: if node.missing_go_to_left:
node_idx = node.left node_idx = node.left
else: else:
node_idx = node.right node_idx = node.right
elif node.is_categorical: elif node.is_categorical:
if in_bitset_2d_memoryview( if in_bitset_2d_memoryview(
binned_left_cat_bitsets, binned_left_cat_bitsets,
data_val, data_val,
node.bitset_idx): node.bitset_idx):
node_idx = node.left node_idx = node.left
else: else:
node_idx = node.right node_idx = node.right
else: else:
if data_val <= node.bin_threshold: if data_val <= node.bin_threshold:
node_idx = node.left node_idx = node.left
else: else:
node_idx = node.right node_idx = node.right
node = nodes[node_idx] node = nodes[node_idx]
def _compute_partial_dependence( def _compute_partial_dependence(
node_struct [:] nodes, node_struct [:] nodes,
const X_DTYPE_C [:, ::1] X, const X_DTYPE_C [:, ::1] X,
const intp_t [:] target_features, const intp_t [:] target_features,
Y_DTYPE_C [:] out Y_DTYPE_C [:] out
): ):
"""Partial dependence of the response on the ``target_features`` set. """Partial dependence of the response on the ``target_features`` set.
For each sample in ``X`` a tree traversal is performed. For each sample in ``X`` a tree traversal is performed.
Each traversal starts from the root with weight 1.0. Each traversal starts from the root with weight 1.0.
At each non-leaf node that splits on a target feature, either At each non-leaf node that splits on a target feature, either
the left child or the right child is visited based on the feature the left child or the right child is visited based on the feature
value of the current sample, and the weight is not modified. value of the current sample, and the weight is not modified.
At each non-leaf node that splits on a complementary feature, At each non-leaf node that splits on a complementary feature,
both children are visited and the weight is multiplied by the fraction both children are visited and the weight is multiplied by the fraction
of training samples which went to each child. of training samples which went to each child.
At each leaf, the value of the node is multiplied by the current At each leaf, the value of the node is multiplied by the current
weight (weights sum to 1 for all visited terminal nodes). weight (weights sum to 1 for all visited terminal nodes).
Parameters Parameters
---------- ----------
nodes : view on array of PREDICTOR_RECORD_DTYPE, shape (n_nodes) nodes : view on array of PREDICTOR_RECORD_DTYPE, shape (n_nodes)
The array representing the predictor tree. The array representing the predictor tree.
X : view on 2d ndarray, shape (n_samples, n_target_features) X : view on 2d ndarray, shape (n_samples, n_target_features)
The grid points on which the partial dependence should be The grid points on which the partial dependence should be
evaluated. evaluated.
target_features : view on 1d ndarray of intp_t, shape (n_target_features) target_features : view on 1d ndarray of intp_t, shape (n_target_features)
The set of target features for which the partial dependence The set of target features for which the partial dependence
should be evaluated. should be evaluated.
out : view on 1d ndarray, shape (n_samples) out : view on 1d ndarray, shape (n_samples)
The value of the partial dependence function on each grid The value of the partial dependence function on each grid
point. point.
""" """
cdef: cdef:
unsigned int current_node_idx unsigned int current_node_idx
unsigned int [:] node_idx_stack = np.zeros(shape=nodes.shape[0], unsigned int [:] node_idx_stack = np.zeros(shape=nodes.shape[0],
dtype=np.uint32) dtype=np.uint32)
Y_DTYPE_C [::1] weight_stack = np.zeros(shape=nodes.shape[0], Y_DTYPE_C [::1] weight_stack = np.zeros(shape=nodes.shape[0],
dtype=Y_DTYPE) dtype=Y_DTYPE)
node_struct * current_node # pointer to avoid copying attributes node_struct * current_node # pointer to avoid copying attributes
unsigned int sample_idx unsigned int sample_idx
intp_t feature_idx intp_t feature_idx
unsigned stack_size unsigned stack_size
Y_DTYPE_C left_sample_frac Y_DTYPE_C left_sample_frac
Y_DTYPE_C current_weight Y_DTYPE_C current_weight
Y_DTYPE_C total_weight # used for sanity check only Y_DTYPE_C total_weight # used for sanity check only
bint is_target_feature bint is_target_feature
for sample_idx in range(X.shape[0]): for sample_idx in range(X.shape[0]):
# init stacks for current sample # init stacks for current sample
stack_size = 1 stack_size = 1
node_idx_stack[0] = 0 # root node node_idx_stack[0] = 0 # root node
weight_stack[0] = 1 # all the samples are in the root node weight_stack[0] = 1 # all the samples are in the root node
total_weight = 0 total_weight = 0
while stack_size > 0: while stack_size > 0:
# pop the stack # pop the stack
stack_size -= 1 stack_size -= 1
current_node_idx = node_idx_stack[stack_size] current_node_idx = node_idx_stack[stack_size]
current_node = &nodes[current_node_idx] current_node = &nodes[current_node_idx]
if current_node.is_leaf: if current_node.is_leaf:
out[sample_idx] += (weight_stack[stack_size] * out[sample_idx] += (weight_stack[stack_size] *
current_node.value) current_node.value)
total_weight += weight_stack[stack_size] total_weight += weight_stack[stack_size]
else: else:
# determine if the split feature is a target feature # determine if the split feature is a target feature
is_target_feature = False is_target_feature = False
for feature_idx in range(target_features.shape[0]): for feature_idx in range(target_features.shape[0]):
if target_features[feature_idx] == current_node.feature_idx: if target_features[feature_idx] == current_node.feature_idx:
is_target_feature = True is_target_feature = True
break break
if is_target_feature: if is_target_feature:
# In this case, we push left or right child on stack # In this case, we push left or right child on stack
if X[sample_idx, feature_idx] <= current_node.num_threshold: if X[sample_idx, feature_idx] <= current_node.num_threshold:
node_idx_stack[stack_size] = current_node.left node_idx_stack[stack_size] = current_node.left
else: else:
node_idx_stack[stack_size] = current_node.right node_idx_stack[stack_size] = current_node.right
stack_size += 1 stack_size += 1
else: else:
# In this case, we push both children onto the stack, # In this case, we push both children onto the stack,
# and give a weight proportional to the number of # and give a weight proportional to the number of
# samples going through each branch. # samples going through each branch.
# push left child # push left child
node_idx_stack[stack_size] = current_node.left node_idx_stack[stack_size] = current_node.left
left_sample_frac = ( left_sample_frac = (
<Y_DTYPE_C> nodes[current_node.left].count / <Y_DTYPE_C> nodes[current_node.left].count /
current_node.count) current_node.count)
current_weight = weight_stack[stack_size] current_weight = weight_stack[stack_size]
weight_stack[stack_size] = current_weight * left_sample_frac weight_stack[stack_size] = current_weight * left_sample_frac
stack_size += 1 stack_size += 1
# push right child # push right child
node_idx_stack[stack_size] = current_node.right node_idx_stack[stack_size] = current_node.right
weight_stack[stack_size] = ( weight_stack[stack_size] = (
current_weight * (1 - left_sample_frac)) current_weight * (1 - left_sample_frac))
stack_size += 1 stack_size += 1
# Sanity check. Should never happen. # Sanity check. Should never happen.
if not (0.999 < total_weight < 1.001): if not (0.999 < total_weight < 1.001):
raise ValueError("Total weight should be 1.0 but was %.9f" %total_weight) raise ValueError("Total weight should be 1.0 but was %.9f" %total_weight)
@@ -1,43 +1,43 @@
from ...utils._typedefs cimport float32_t, float64_t, intp_t, uint8_t, uint32_t from ...utils._typedefs cimport float32_t, float64_t, intp_t, uint8_t, uint32_t
ctypedef float64_t X_DTYPE_C ctypedef float64_t X_DTYPE_C
ctypedef uint8_t X_BINNED_DTYPE_C ctypedef uint8_t X_BINNED_DTYPE_C
ctypedef float64_t Y_DTYPE_C ctypedef float64_t Y_DTYPE_C
ctypedef float32_t G_H_DTYPE_C ctypedef float32_t G_H_DTYPE_C
ctypedef uint32_t BITSET_INNER_DTYPE_C ctypedef uint32_t BITSET_INNER_DTYPE_C
ctypedef BITSET_INNER_DTYPE_C[8] BITSET_DTYPE_C ctypedef BITSET_INNER_DTYPE_C[8] BITSET_DTYPE_C
cdef packed struct hist_struct: cdef packed struct hist_struct:
# Same as histogram dtype but we need a struct to declare views. It needs # Same as histogram dtype but we need a struct to declare views. It needs
# to be packed since by default numpy dtypes aren't aligned # to be packed since by default numpy dtypes aren't aligned
Y_DTYPE_C sum_gradients Y_DTYPE_C sum_gradients
Y_DTYPE_C sum_hessians Y_DTYPE_C sum_hessians
unsigned int count unsigned int count
cdef packed struct node_struct: cdef packed struct node_struct:
# Equivalent struct to PREDICTOR_RECORD_DTYPE to use in memory views. It # Equivalent struct to PREDICTOR_RECORD_DTYPE to use in memory views. It
# needs to be packed since by default numpy dtypes aren't aligned # needs to be packed since by default numpy dtypes aren't aligned
Y_DTYPE_C value Y_DTYPE_C value
unsigned int count unsigned int count
intp_t feature_idx intp_t feature_idx
X_DTYPE_C num_threshold X_DTYPE_C num_threshold
uint8_t missing_go_to_left uint8_t missing_go_to_left
unsigned int left unsigned int left
unsigned int right unsigned int right
Y_DTYPE_C gain Y_DTYPE_C gain
unsigned int depth unsigned int depth
uint8_t is_leaf uint8_t is_leaf
X_BINNED_DTYPE_C bin_threshold X_BINNED_DTYPE_C bin_threshold
uint8_t is_categorical uint8_t is_categorical
# The index of the corresponding bitsets in the Predictor's bitset arrays. # The index of the corresponding bitsets in the Predictor's bitset arrays.
# Only used if is_categorical is True # Only used if is_categorical is True
unsigned int bitset_idx unsigned int bitset_idx
cpdef enum MonotonicConstraint: cpdef enum MonotonicConstraint:
NO_CST = 0 NO_CST = 0
POS = 1 POS = 1
NEG = -1 NEG = -1
@@ -1,44 +1,44 @@
import numpy as np import numpy as np
# Y_DYTPE is the dtype to which the targets y are converted to. This is also # Y_DYTPE is the dtype to which the targets y are converted to. This is also
# dtype for leaf values, gains, and sums of gradients / hessians. The gradients # dtype for leaf values, gains, and sums of gradients / hessians. The gradients
# and hessians arrays are stored as floats to avoid using too much memory. # and hessians arrays are stored as floats to avoid using too much memory.
Y_DTYPE = np.float64 Y_DTYPE = np.float64
X_DTYPE = np.float64 X_DTYPE = np.float64
X_BINNED_DTYPE = np.uint8 # hence max_bins == 256 X_BINNED_DTYPE = np.uint8 # hence max_bins == 256
# dtype for gradients and hessians arrays # dtype for gradients and hessians arrays
G_H_DTYPE = np.float32 G_H_DTYPE = np.float32
X_BITSET_INNER_DTYPE = np.uint32 X_BITSET_INNER_DTYPE = np.uint32
# Note that we use Y_DTYPE=float64 to avoid issues with floating point precision when # Note that we use Y_DTYPE=float64 to avoid issues with floating point precision when
# summing gradients and hessians (both float32). Those are difficult to protect via # summing gradients and hessians (both float32). Those are difficult to protect via
# tools like (Kahan-) Neumaier summation as in CPython, see # tools like (Kahan-) Neumaier summation as in CPython, see
# https://github.com/python/cpython/issues/100425, or pairwise summation as numpy, see # https://github.com/python/cpython/issues/100425, or pairwise summation as numpy, see
# https://github.com/numpy/numpy/pull/3685, due to the way histograms are summed # https://github.com/numpy/numpy/pull/3685, due to the way histograms are summed
# (number of additions per bin is not known in advance). See also comment in # (number of additions per bin is not known in advance). See also comment in
# _subtract_histograms. # _subtract_histograms.
HISTOGRAM_DTYPE = np.dtype([ HISTOGRAM_DTYPE = np.dtype([
('sum_gradients', Y_DTYPE), # sum of sample gradients in bin ('sum_gradients', Y_DTYPE), # sum of sample gradients in bin
('sum_hessians', Y_DTYPE), # sum of sample hessians in bin ('sum_hessians', Y_DTYPE), # sum of sample hessians in bin
('count', np.uint32), # number of samples in bin ('count', np.uint32), # number of samples in bin
]) ])
PREDICTOR_RECORD_DTYPE = np.dtype([ PREDICTOR_RECORD_DTYPE = np.dtype([
('value', Y_DTYPE), ('value', Y_DTYPE),
('count', np.uint32), ('count', np.uint32),
('feature_idx', np.intp), ('feature_idx', np.intp),
('num_threshold', X_DTYPE), ('num_threshold', X_DTYPE),
('missing_go_to_left', np.uint8), ('missing_go_to_left', np.uint8),
('left', np.uint32), ('left', np.uint32),
('right', np.uint32), ('right', np.uint32),
('gain', Y_DTYPE), ('gain', Y_DTYPE),
('depth', np.uint32), ('depth', np.uint32),
('is_leaf', np.uint8), ('is_leaf', np.uint8),
('bin_threshold', X_BINNED_DTYPE), ('bin_threshold', X_BINNED_DTYPE),
('is_categorical', np.uint8), ('is_categorical', np.uint8),
# The index of the corresponding bitsets in the Predictor's bitset arrays. # The index of the corresponding bitsets in the Predictor's bitset arrays.
# Only used if is_categorical is True # Only used if is_categorical is True
('bitset_idx', np.uint32) ('bitset_idx', np.uint32)
]) ])
ALMOST_INF = 1e300 # see LightGBM AvoidInf() ALMOST_INF = 1e300 # see LightGBM AvoidInf()
@@ -1,20 +1,20 @@
hist_gradient_boosting_extension_metadata = { hist_gradient_boosting_extension_metadata = {
'_gradient_boosting': {'sources': [cython_gen.process('_gradient_boosting.pyx')], '_gradient_boosting': {'sources': [cython_gen.process('_gradient_boosting.pyx')],
'dependencies': [openmp_dep]}, 'dependencies': [openmp_dep]},
'histogram': {'sources': [cython_gen.process('histogram.pyx')], 'dependencies': [openmp_dep]}, 'histogram': {'sources': [cython_gen.process('histogram.pyx')], 'dependencies': [openmp_dep]},
'splitting': {'sources': [cython_gen.process('splitting.pyx')], 'dependencies': [openmp_dep]}, 'splitting': {'sources': [cython_gen.process('splitting.pyx')], 'dependencies': [openmp_dep]},
'_binning': {'sources': [cython_gen.process('_binning.pyx')], 'dependencies': [openmp_dep]}, '_binning': {'sources': [cython_gen.process('_binning.pyx')], 'dependencies': [openmp_dep]},
'_predictor': {'sources': [cython_gen.process('_predictor.pyx')], 'dependencies': [openmp_dep]}, '_predictor': {'sources': [cython_gen.process('_predictor.pyx')], 'dependencies': [openmp_dep]},
'_bitset': {'sources': [cython_gen.process('_bitset.pyx')]}, '_bitset': {'sources': [cython_gen.process('_bitset.pyx')]},
'common': {'sources': [cython_gen.process('common.pyx')]}, 'common': {'sources': [cython_gen.process('common.pyx')]},
} }
foreach ext_name, ext_dict : hist_gradient_boosting_extension_metadata foreach ext_name, ext_dict : hist_gradient_boosting_extension_metadata
py.extension_module( py.extension_module(
ext_name, ext_name,
ext_dict.get('sources'), ext_dict.get('sources'),
dependencies: ext_dict.get('dependencies', []), dependencies: ext_dict.get('dependencies', []),
subdir: 'sklearn/ensemble/_hist_gradient_boosting', subdir: 'sklearn/ensemble/_hist_gradient_boosting',
install: true install: true
) )
endforeach endforeach
@@ -1,9 +1,9 @@
py.extension_module( py.extension_module(
'_gradient_boosting', '_gradient_boosting',
[cython_gen.process('_gradient_boosting.pyx')] + utils_cython_tree, [cython_gen.process('_gradient_boosting.pyx')] + utils_cython_tree,
dependencies: [np_dep], dependencies: [np_dep],
subdir: 'sklearn/ensemble', subdir: 'sklearn/ensemble',
install: true install: true
) )
subdir('_hist_gradient_boosting') subdir('_hist_gradient_boosting')
@@ -1,7 +1,7 @@
This directory contains bundled external dependencies that are updated This directory contains bundled external dependencies that are updated
every once in a while. every once in a while.
Note for distribution packagers: if you want to remove the duplicated Note for distribution packagers: if you want to remove the duplicated
code and depend on a packaged version, we suggest that you simply do a code and depend on a packaged version, we suggest that you simply do a
symbolic link in this directory. symbolic link in this directory.
@@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2022 Consortium for Python Data API Standards Copyright (c) 2022 Consortium for Python Data API Standards
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
@@ -1 +1 @@
Update this directory using maint_tools/vendor_array_api_compat.sh Update this directory using maint_tools/vendor_array_api_compat.sh
@@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2024 Consortium for Python Data API Standards Copyright (c) 2024 Consortium for Python Data API Standards
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
@@ -1 +1 @@
Update this directory using maint_tools/vendor_array_api_extra.sh Update this directory using maint_tools/vendor_array_api_extra.sh
@@ -1,40 +1,40 @@
"""Static type stubs for `_compat.py`.""" """Static type stubs for `_compat.py`."""
# https://github.com/scikit-learn/scikit-learn/pull/27910#issuecomment-2568023972 # https://github.com/scikit-learn/scikit-learn/pull/27910#issuecomment-2568023972
from __future__ import annotations from __future__ import annotations
from types import ModuleType from types import ModuleType
# TODO import from typing (requires Python >=3.13) # TODO import from typing (requires Python >=3.13)
from typing_extensions import TypeIs from typing_extensions import TypeIs
from ._typing import Array, Device from ._typing import Array, Device
# pylint: disable=missing-class-docstring,unused-argument # pylint: disable=missing-class-docstring,unused-argument
class Namespace(ModuleType): class Namespace(ModuleType):
def device(self, x: Array, /) -> Device: ... def device(self, x: Array, /) -> Device: ...
def array_namespace( def array_namespace(
*xs: Array | complex | None, *xs: Array | complex | None,
api_version: str | None = None, api_version: str | None = None,
use_compat: bool | None = None, use_compat: bool | None = None,
) -> Namespace: ... ) -> Namespace: ...
def device(x: Array, /) -> Device: ... def device(x: Array, /) -> Device: ...
def is_array_api_obj(x: object, /) -> TypeIs[Array]: ... def is_array_api_obj(x: object, /) -> TypeIs[Array]: ...
def is_array_api_strict_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_array_api_strict_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_cupy_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_cupy_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_dask_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_dask_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_jax_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_jax_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_numpy_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_numpy_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_pydata_sparse_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_pydata_sparse_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_torch_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ... def is_torch_namespace(xp: ModuleType, /) -> TypeIs[Namespace]: ...
def is_cupy_array(x: object, /) -> TypeIs[Array]: ... def is_cupy_array(x: object, /) -> TypeIs[Array]: ...
def is_dask_array(x: object, /) -> TypeIs[Array]: ... def is_dask_array(x: object, /) -> TypeIs[Array]: ...
def is_jax_array(x: object, /) -> TypeIs[Array]: ... def is_jax_array(x: object, /) -> TypeIs[Array]: ...
def is_numpy_array(x: object, /) -> TypeIs[Array]: ... def is_numpy_array(x: object, /) -> TypeIs[Array]: ...
def is_pydata_sparse_array(x: object, /) -> TypeIs[Array]: ... def is_pydata_sparse_array(x: object, /) -> TypeIs[Array]: ...
def is_torch_array(x: object, /) -> TypeIs[Array]: ... def is_torch_array(x: object, /) -> TypeIs[Array]: ...
def is_lazy_array(x: object, /) -> TypeIs[Array]: ... def is_lazy_array(x: object, /) -> TypeIs[Array]: ...
def is_writeable_array(x: object, /) -> TypeIs[Array]: ... def is_writeable_array(x: object, /) -> TypeIs[Array]: ...
def size(x: Array, /) -> int | None: ... def size(x: Array, /) -> int | None: ...
@@ -1,105 +1,105 @@
"""Static typing helpers.""" """Static typing helpers."""
from __future__ import annotations from __future__ import annotations
from types import EllipsisType from types import EllipsisType
from typing import Protocol, TypeAlias from typing import Protocol, TypeAlias
# TODO import from typing (requires Python >=3.12) # TODO import from typing (requires Python >=3.12)
from typing_extensions import override from typing_extensions import override
# TODO: use array-api-typing once it is available # TODO: use array-api-typing once it is available
class Array(Protocol): # pylint: disable=missing-class-docstring class Array(Protocol): # pylint: disable=missing-class-docstring
# Unary operations # Unary operations
def __abs__(self) -> Array: ... def __abs__(self) -> Array: ...
def __pos__(self) -> Array: ... def __pos__(self) -> Array: ...
def __neg__(self) -> Array: ... def __neg__(self) -> Array: ...
def __invert__(self) -> Array: ... def __invert__(self) -> Array: ...
# Binary operations # Binary operations
def __add__(self, other: Array | complex, /) -> Array: ... def __add__(self, other: Array | complex, /) -> Array: ...
def __sub__(self, other: Array | complex, /) -> Array: ... def __sub__(self, other: Array | complex, /) -> Array: ...
def __mul__(self, other: Array | complex, /) -> Array: ... def __mul__(self, other: Array | complex, /) -> Array: ...
def __truediv__(self, other: Array | complex, /) -> Array: ... def __truediv__(self, other: Array | complex, /) -> Array: ...
def __floordiv__(self, other: Array | complex, /) -> Array: ... def __floordiv__(self, other: Array | complex, /) -> Array: ...
def __mod__(self, other: Array | complex, /) -> Array: ... def __mod__(self, other: Array | complex, /) -> Array: ...
def __pow__(self, other: Array | complex, /) -> Array: ... def __pow__(self, other: Array | complex, /) -> Array: ...
def __matmul__(self, other: Array, /) -> Array: ... def __matmul__(self, other: Array, /) -> Array: ...
def __and__(self, other: Array | int, /) -> Array: ... def __and__(self, other: Array | int, /) -> Array: ...
def __or__(self, other: Array | int, /) -> Array: ... def __or__(self, other: Array | int, /) -> Array: ...
def __xor__(self, other: Array | int, /) -> Array: ... def __xor__(self, other: Array | int, /) -> Array: ...
def __lshift__(self, other: Array | int, /) -> Array: ... def __lshift__(self, other: Array | int, /) -> Array: ...
def __rshift__(self, other: Array | int, /) -> Array: ... def __rshift__(self, other: Array | int, /) -> Array: ...
def __lt__(self, other: Array | complex, /) -> Array: ... def __lt__(self, other: Array | complex, /) -> Array: ...
def __le__(self, other: Array | complex, /) -> Array: ... def __le__(self, other: Array | complex, /) -> Array: ...
def __gt__(self, other: Array | complex, /) -> Array: ... def __gt__(self, other: Array | complex, /) -> Array: ...
def __ge__(self, other: Array | complex, /) -> Array: ... def __ge__(self, other: Array | complex, /) -> Array: ...
@override @override
def __eq__(self, other: Array | complex, /) -> Array: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def __eq__(self, other: Array | complex, /) -> Array: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
@override @override
def __ne__(self, other: Array | complex, /) -> Array: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] def __ne__(self, other: Array | complex, /) -> Array: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
# Reflected operations # Reflected operations
def __radd__(self, other: Array | complex, /) -> Array: ... def __radd__(self, other: Array | complex, /) -> Array: ...
def __rsub__(self, other: Array | complex, /) -> Array: ... def __rsub__(self, other: Array | complex, /) -> Array: ...
def __rmul__(self, other: Array | complex, /) -> Array: ... def __rmul__(self, other: Array | complex, /) -> Array: ...
def __rtruediv__(self, other: Array | complex, /) -> Array: ... def __rtruediv__(self, other: Array | complex, /) -> Array: ...
def __rfloordiv__(self, other: Array | complex, /) -> Array: ... def __rfloordiv__(self, other: Array | complex, /) -> Array: ...
def __rmod__(self, other: Array | complex, /) -> Array: ... def __rmod__(self, other: Array | complex, /) -> Array: ...
def __rpow__(self, other: Array | complex, /) -> Array: ... def __rpow__(self, other: Array | complex, /) -> Array: ...
def __rmatmul__(self, other: Array, /) -> Array: ... def __rmatmul__(self, other: Array, /) -> Array: ...
def __rand__(self, other: Array | int, /) -> Array: ... def __rand__(self, other: Array | int, /) -> Array: ...
def __ror__(self, other: Array | int, /) -> Array: ... def __ror__(self, other: Array | int, /) -> Array: ...
def __rxor__(self, other: Array | int, /) -> Array: ... def __rxor__(self, other: Array | int, /) -> Array: ...
def __rlshift__(self, other: Array | int, /) -> Array: ... def __rlshift__(self, other: Array | int, /) -> Array: ...
def __rrshift__(self, other: Array | int, /) -> Array: ... def __rrshift__(self, other: Array | int, /) -> Array: ...
# Attributes # Attributes
@property @property
def dtype(self) -> DType: ... def dtype(self) -> DType: ...
@property @property
def device(self) -> Device: ... def device(self) -> Device: ...
@property @property
def mT(self) -> Array: ... # pylint: disable=invalid-name def mT(self) -> Array: ... # pylint: disable=invalid-name
@property @property
def ndim(self) -> int: ... def ndim(self) -> int: ...
@property @property
def shape(self) -> tuple[int | None, ...]: ... def shape(self) -> tuple[int | None, ...]: ...
@property @property
def size(self) -> int | None: ... def size(self) -> int | None: ...
@property @property
def T(self) -> Array: ... # pylint: disable=invalid-name def T(self) -> Array: ... # pylint: disable=invalid-name
# Collection operations (note: an Array does not have to be Sized or Iterable) # Collection operations (note: an Array does not have to be Sized or Iterable)
def __getitem__(self, key: GetIndex, /) -> Array: ... def __getitem__(self, key: GetIndex, /) -> Array: ...
def __setitem__(self, key: SetIndex, value: Array | complex, /) -> None: ... def __setitem__(self, key: SetIndex, value: Array | complex, /) -> None: ...
# Materialization methods (may raise on lazy arrays) # Materialization methods (may raise on lazy arrays)
def __bool__(self) -> bool: ... def __bool__(self) -> bool: ...
def __complex__(self) -> complex: ... def __complex__(self) -> complex: ...
def __float__(self) -> float: ... def __float__(self) -> float: ...
def __index__(self) -> int: ... def __index__(self) -> int: ...
def __int__(self) -> int: ... def __int__(self) -> int: ...
# Misc methods (frequently not implemented in Arrays wrapped by array-api-compat) # Misc methods (frequently not implemented in Arrays wrapped by array-api-compat)
# def __array_namespace__(*, api_version: str | None) -> ModuleType: ... # def __array_namespace__(*, api_version: str | None) -> ModuleType: ...
# def __dlpack__( # def __dlpack__(
# *, # *,
# stream: int | Any | None = None, # stream: int | Any | None = None,
# max_version: tuple[int, int] | None = None, # max_version: tuple[int, int] | None = None,
# dl_device: tuple[int, int] | None = None, # tuple[Enum, int] # dl_device: tuple[int, int] | None = None, # tuple[Enum, int]
# copy: bool | None = None, # copy: bool | None = None,
# ) -> Any: ... # ) -> Any: ...
# def __dlpack_device__() -> tuple[int, int]: ... # tuple[Enum, int] # def __dlpack_device__() -> tuple[int, int]: ... # tuple[Enum, int]
# def to_device(device: Device, /, *, stream: int | Any | None = None) -> Array: ... # def to_device(device: Device, /, *, stream: int | Any | None = None) -> Array: ...
class DType(Protocol): # pylint: disable=missing-class-docstring class DType(Protocol): # pylint: disable=missing-class-docstring
pass pass
class Device(Protocol): # pylint: disable=missing-class-docstring class Device(Protocol): # pylint: disable=missing-class-docstring
pass pass
SetIndex: TypeAlias = ( # type: ignore[explicit-any] SetIndex: TypeAlias = ( # type: ignore[explicit-any]
int | slice | EllipsisType | Array | tuple[int | slice | EllipsisType | Array, ...] int | slice | EllipsisType | Array | tuple[int | slice | EllipsisType | Array, ...]
) )
GetIndex: TypeAlias = ( # type: ignore[explicit-any] GetIndex: TypeAlias = ( # type: ignore[explicit-any]
SetIndex | None | tuple[int | slice | EllipsisType | None | Array, ...] SetIndex | None | tuple[int | slice | EllipsisType | None | Array, ...]
) )
__all__ = ["Array", "DType", "Device", "GetIndex", "SetIndex"] __all__ = ["Array", "DType", "Device", "GetIndex", "SetIndex"]

Some files were not shown because too many files have changed in this diff Show More