ashlr-plugin 1.36.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +39 -0
- package/.claude-plugin/plugin.json +36 -0
- package/.codex/agents/README.md +9 -0
- package/.codex/agents/ashlr-explorer.md +36 -0
- package/.codex/agents/ashlr-worker.md +38 -0
- package/.codex-plugin/plugin.json +45 -0
- package/.mcp.json +14 -0
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/agents/ashlr-code.md +69 -0
- package/agents/ashlr-explore.md +44 -0
- package/agents/ashlr-plan.md +54 -0
- package/bench/fixtures/notebook-large.ipynb +660 -0
- package/bench/fixtures/notebook-small.ipynb +39 -0
- package/bench/fixtures/tasklist-closed.json +62 -0
- package/bench/fixtures/tasklist-large-mixed.json +27 -0
- package/bench/fixtures/tasklist-sprint-open.json +79 -0
- package/bench/fixtures/websearch-bun-runtime.json +32 -0
- package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
- package/bench/fixtures/websearch-typescript-generics.json +38 -0
- package/bench/refs/node-sdk/.refrev +1 -0
- package/bench/refs/node-sdk/README.md +17 -0
- package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
- package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
- package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
- package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
- package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
- package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
- package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
- package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
- package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
- package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
- package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
- package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
- package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
- package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
- package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
- package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
- package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
- package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
- package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
- package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
- package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
- package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
- package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
- package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
- package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
- package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
- package/bench/refs/python-lib/.refrev +1 -0
- package/bench/refs/python-lib/README.md +18 -0
- package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
- package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
- package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
- package/bench/refs/python-lib/pandas/core/api.py +138 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
- package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
- package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
- package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
- package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
- package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
- package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
- package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
- package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
- package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
- package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
- package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
- package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
- package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
- package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
- package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
- package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
- package/bench/refs/rust-project/.refrev +1 -0
- package/bench/refs/rust-project/README.md +17 -0
- package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
- package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
- package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
- package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
- package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
- package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
- package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
- package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
- package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
- package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
- package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
- package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
- package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
- package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
- package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
- package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
- package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
- package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
- package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
- package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
- package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
- package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
- package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
- package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
- package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
- package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
- package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
- package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
- package/commands/_spawn-patterns.json +32 -0
- package/commands/ashlr-allow.md +21 -0
- package/commands/ashlr-benchmark.md +53 -0
- package/commands/ashlr-brief.md +97 -0
- package/commands/ashlr-budget.md +76 -0
- package/commands/ashlr-compact.md +54 -0
- package/commands/ashlr-dashboard.md +37 -0
- package/commands/ashlr-doctor.md +56 -0
- package/commands/ashlr-eco-mode.md +92 -0
- package/commands/ashlr-genome-init.md +24 -0
- package/commands/ashlr-genome-keygen.md +36 -0
- package/commands/ashlr-genome-loop.md +118 -0
- package/commands/ashlr-genome-push.md +36 -0
- package/commands/ashlr-genome-rewrap.md +48 -0
- package/commands/ashlr-genome-team-init.md +67 -0
- package/commands/ashlr-handoff.md +23 -0
- package/commands/ashlr-help.md +86 -0
- package/commands/ashlr-hook-timings.md +30 -0
- package/commands/ashlr-leaderboard.md +50 -0
- package/commands/ashlr-ollama-setup.md +39 -0
- package/commands/ashlr-orchestrate-status.md +72 -0
- package/commands/ashlr-orchestrate.md +83 -0
- package/commands/ashlr-parallelize.md +63 -0
- package/commands/ashlr-report-crash.md +33 -0
- package/commands/ashlr-resume.md +72 -0
- package/commands/ashlr-savings.md +24 -0
- package/commands/ashlr-settings.md +50 -0
- package/commands/ashlr-spawn.md +61 -0
- package/commands/ashlr-start.md +60 -0
- package/commands/ashlr-status.md +64 -0
- package/commands/ashlr-team-invite.md +47 -0
- package/commands/ashlr-tier.md +122 -0
- package/commands/ashlr-tour.md +60 -0
- package/commands/ashlr-update.md +155 -0
- package/commands/ashlr-upgrade.md +96 -0
- package/commands/goal.md +58 -0
- package/commands/loop.md +40 -0
- package/docs/CNAME +1 -0
- package/docs/PRO_TIER.md +104 -0
- package/docs/README.md +21 -0
- package/docs/architecture.md +415 -0
- package/docs/ashlr-pipe.md +161 -0
- package/docs/assets/logo.svg +19 -0
- package/docs/assets/og.png +0 -0
- package/docs/assets/og.svg +41 -0
- package/docs/benchmarks-v2.json +355 -0
- package/docs/benchmarks.json +102 -0
- package/docs/benchmarks.md +211 -0
- package/docs/billing.md +185 -0
- package/docs/cloud-genome.md +155 -0
- package/docs/competitive-wozcode.md +92 -0
- package/docs/deploy.md +325 -0
- package/docs/email-templates.md +113 -0
- package/docs/embed-tuning.md +103 -0
- package/docs/genome.md +120 -0
- package/docs/github-oauth-onboarding.md +127 -0
- package/docs/hero-video-script.md +129 -0
- package/docs/index.html +2262 -0
- package/docs/install-prompt.md +78 -0
- package/docs/install-windows.md +132 -0
- package/docs/install.ps1 +171 -0
- package/docs/install.sh +146 -0
- package/docs/launch/README.md +114 -0
- package/docs/launch/hn.md +49 -0
- package/docs/launch/reddit.md +45 -0
- package/docs/launch/x.md +65 -0
- package/docs/launch-post.md +112 -0
- package/docs/legal.md +78 -0
- package/docs/llm-providers.md +99 -0
- package/docs/multi-host-mcp.md +217 -0
- package/docs/multi-turn-architecture.md +108 -0
- package/docs/operations/deployment.md +108 -0
- package/docs/operations/error-monitoring.md +70 -0
- package/docs/operations/weekly-digest.md +70 -0
- package/docs/operations.md +494 -0
- package/docs/platform-support.md +133 -0
- package/docs/policy-packs.md +188 -0
- package/docs/press-kit/fact-sheet.md +78 -0
- package/docs/press-kit/founder-bio.md +3 -0
- package/docs/press-kit/logo.svg +33 -0
- package/docs/press-kit/screenshots.md +146 -0
- package/docs/pricing.md +251 -0
- package/docs/pro-backend-architecture.md +523 -0
- package/docs/producthunt.md +52 -0
- package/docs/router-migration.md +110 -0
- package/docs/session-log-schema.md +68 -0
- package/docs/social.md +95 -0
- package/docs/team-genome.md +528 -0
- package/docs/telemetry.md +194 -0
- package/docs/test-isolation.md +100 -0
- package/docs/upgrade.md +159 -0
- package/hooks/_hook-errors.ts +87 -0
- package/hooks/_hook-perf-emit.ts +113 -0
- package/hooks/_hook-timing-ledger.ts +1133 -0
- package/hooks/_nudge-throttle.ts +127 -0
- package/hooks/_recent-blocks.ts +127 -0
- package/hooks/audit-upload.ts +86 -0
- package/hooks/codex-hooks.json +171 -0
- package/hooks/commit-attribution.ts +179 -0
- package/hooks/edit-batching-nudge.ts +154 -0
- package/hooks/genome-scribe-hook.ts +219 -0
- package/hooks/hooks.json +294 -0
- package/hooks/orient-nudge-hook.ts +242 -0
- package/hooks/policy-enforce.ts +214 -0
- package/hooks/post-tool-use-embedding.ts +93 -0
- package/hooks/post-tool-use-genome.sh +37 -0
- package/hooks/post-tool-use-genome.ts +69 -0
- package/hooks/posttooluse-correlate.ts +122 -0
- package/hooks/posttooluse-dedupe-output.ts +241 -0
- package/hooks/posttooluse-genome-refresh.ts +223 -0
- package/hooks/posttooluse-native-nudge.ts +205 -0
- package/hooks/posttooluse-stale-result.ts +277 -0
- package/hooks/pretooluse-bash.ts +63 -0
- package/hooks/pretooluse-budget-guard.ts +318 -0
- package/hooks/pretooluse-common.ts +985 -0
- package/hooks/pretooluse-eco-router.ts +196 -0
- package/hooks/pretooluse-edit.sh +93 -0
- package/hooks/pretooluse-edit.ts +197 -0
- package/hooks/pretooluse-glob.ts +70 -0
- package/hooks/pretooluse-grep.sh +85 -0
- package/hooks/pretooluse-grep.ts +96 -0
- package/hooks/pretooluse-notebookedit.ts +121 -0
- package/hooks/pretooluse-read.sh +118 -0
- package/hooks/pretooluse-read.ts +118 -0
- package/hooks/pretooluse-task.ts +130 -0
- package/hooks/pretooluse-webfetch.ts +66 -0
- package/hooks/pretooluse-websearch.ts +85 -0
- package/hooks/pretooluse-write.ts +128 -0
- package/hooks/pulse-emit.ts +197 -0
- package/hooks/session-end-consolidate.sh +35 -0
- package/hooks/session-end-consolidate.ts +188 -0
- package/hooks/session-end-stats-gc.ts +80 -0
- package/hooks/session-log-append.sh +94 -0
- package/hooks/session-log-append.ts +116 -0
- package/hooks/session-start.ts +993 -0
- package/hooks/sessionend-hook-health-nudge.ts +451 -0
- package/hooks/sessionend-savings-nudge.ts +318 -0
- package/hooks/sessionend-status-line-nudge.ts +296 -0
- package/hooks/sessionstart-brief.ts +140 -0
- package/hooks/sessionstart-cost-refactor.ts +95 -0
- package/hooks/sessionstart-efficient.ts +110 -0
- package/hooks/sessionstart-genome-author.ts +93 -0
- package/hooks/sessionstart-lean-tools.ts +98 -0
- package/hooks/sessionstart-search.ts +97 -0
- package/hooks/stop-accounting.ts +152 -0
- package/hooks/subagent-stop-rollup.ts +162 -0
- package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
- package/package.json +56 -0
- package/ports/README.md +108 -0
- package/ports/cursor/mcp.json +11 -0
- package/ports/goose/recipe.yaml +26 -0
- package/scripts/ashlr-mcp.ts +40 -0
- package/scripts/auto-update.ts +177 -0
- package/scripts/baseline-scan.ts +831 -0
- package/scripts/bench-orchestrate.ts +377 -0
- package/scripts/benchmark-refs.ts +292 -0
- package/scripts/bootstrap.mjs +100 -0
- package/scripts/brief-eval.ts +234 -0
- package/scripts/bump-version.ts +82 -0
- package/scripts/bun-resolve.d.mts +11 -0
- package/scripts/bun-resolve.mjs +59 -0
- package/scripts/calibrate-grep.ts +467 -0
- package/scripts/capture-grep-workload.ts +260 -0
- package/scripts/check-tier.ts +61 -0
- package/scripts/check-version-sync.ts +97 -0
- package/scripts/cli-orchestrate-status.ts +386 -0
- package/scripts/cli-orchestrate.ts +303 -0
- package/scripts/cli.ts +478 -0
- package/scripts/cloud-smoke-test.ts +313 -0
- package/scripts/coach-report.ts +373 -0
- package/scripts/context-db-migrate.ts +137 -0
- package/scripts/context-status.ts +43 -0
- package/scripts/demo-run.ts +255 -0
- package/scripts/deploy-smoke.sh +140 -0
- package/scripts/doctor.ts +992 -0
- package/scripts/embed-file-worker.ts +104 -0
- package/scripts/embed-tune.ts +308 -0
- package/scripts/errors-report.ts +428 -0
- package/scripts/find-test-leak.ts +69 -0
- package/scripts/generate-badge.ts +357 -0
- package/scripts/genome-auto-consolidate.ts +533 -0
- package/scripts/genome-auto-propose.ts +578 -0
- package/scripts/genome-cloud-pull.ts +363 -0
- package/scripts/genome-cloud-push.ts +382 -0
- package/scripts/genome-cloud-sync.ts +587 -0
- package/scripts/genome-commit-watcher.ts +295 -0
- package/scripts/genome-init.ts +986 -0
- package/scripts/genome-key.ts +233 -0
- package/scripts/genome-keygen.ts +168 -0
- package/scripts/genome-link.ts +59 -0
- package/scripts/genome-refresh-worker.ts +364 -0
- package/scripts/genome-rewrap.ts +90 -0
- package/scripts/genome-synthesizer.ts +679 -0
- package/scripts/genome-team-init.ts +312 -0
- package/scripts/handoff-pack.ts +438 -0
- package/scripts/hook-bootstrap.mjs +75 -0
- package/scripts/hook-timing-reader.ts +371 -0
- package/scripts/hook-timings-report.ts +554 -0
- package/scripts/install-genome-hooks.ts +196 -0
- package/scripts/install-onnx-model.ts +154 -0
- package/scripts/install-permissions.ts +276 -0
- package/scripts/install-status-line.ts +150 -0
- package/scripts/mcp-entrypoint.sh +78 -0
- package/scripts/mcp-entrypoint.ts +130 -0
- package/scripts/mdx-angle-bracket-lint.ts +152 -0
- package/scripts/measure-cold-start.ts +126 -0
- package/scripts/migrate-stats-to-sqlite.ts +86 -0
- package/scripts/ollama-setup.ts +498 -0
- package/scripts/onboarding-wizard.ts +1691 -0
- package/scripts/orchestrate-emit-telemetry.ts +177 -0
- package/scripts/orchestrate-expand.ts +517 -0
- package/scripts/orchestrate-run.ts +744 -0
- package/scripts/print-legend.ts +50 -0
- package/scripts/product-metadata.ts +38 -0
- package/scripts/prune-old-memories.ts +208 -0
- package/scripts/publish.sh +88 -0
- package/scripts/read-calibration.ts +122 -0
- package/scripts/report-crash.ts +226 -0
- package/scripts/run-benchmark.ts +750 -0
- package/scripts/run-root-tests.ts +28 -0
- package/scripts/savings-dashboard.ts +1258 -0
- package/scripts/savings-report-extras.ts +447 -0
- package/scripts/savings-status-line.ts +873 -0
- package/scripts/session-event-emit.ts +266 -0
- package/scripts/session-greet.ts +686 -0
- package/scripts/session-log-report.ts +551 -0
- package/scripts/session-resume.ts +614 -0
- package/scripts/set-hook-mode.ts +83 -0
- package/scripts/smoke-cross-platform.ts +117 -0
- package/scripts/smoke-realtime.ts +257 -0
- package/scripts/smoke-tools.ts +21 -0
- package/scripts/stats-cloud-pull.ts +213 -0
- package/scripts/stats-cloud-sync.ts +261 -0
- package/scripts/telemetry-flush.ts +160 -0
- package/scripts/telemetry-status.ts +266 -0
- package/scripts/timing-budget.ts +35 -0
- package/scripts/ui-animation.ts +120 -0
- package/scripts/ui.ts +431 -0
- package/scripts/upgrade-flow.ts +775 -0
- package/scripts/weekly-digest.ts +394 -0
- package/servers/_accounting.ts +78 -0
- package/servers/_ask-router.ts +84 -0
- package/servers/_ast-chunker.ts +434 -0
- package/servers/_ast-helpers.ts +183 -0
- package/servers/_ast-languages.ts +153 -0
- package/servers/_ast-refactor/_shared.ts +221 -0
- package/servers/_ast-refactor/cross-file-rename.ts +560 -0
- package/servers/_ast-refactor/extract-function.ts +357 -0
- package/servers/_ast-refactor/file-local-rename.ts +172 -0
- package/servers/_ast-refactor.ts +41 -0
- package/servers/_ast-skeleton.ts +107 -0
- package/servers/_bash-summarizers-registry.ts +1096 -0
- package/servers/_crash-dump.ts +210 -0
- package/servers/_cwd-clamp.ts +342 -0
- package/servers/_edit-log.ts +28 -0
- package/servers/_edit-match.ts +236 -0
- package/servers/_edit-validate.ts +196 -0
- package/servers/_embed-calibration.ts +123 -0
- package/servers/_embedding-cache.ts +401 -0
- package/servers/_embedding-model.ts +329 -0
- package/servers/_events.ts +113 -0
- package/servers/_genome-cache.ts +154 -0
- package/servers/_genome-commits.ts +172 -0
- package/servers/_genome-crypto-v2.ts +242 -0
- package/servers/_genome-crypto.ts +259 -0
- package/servers/_genome-discoveries.ts +140 -0
- package/servers/_genome-embed-populator.ts +216 -0
- package/servers/_genome-freshness.ts +134 -0
- package/servers/_genome-issues.ts +171 -0
- package/servers/_genome-live.ts +263 -0
- package/servers/_genome-prs.ts +188 -0
- package/servers/_genome-search.ts +479 -0
- package/servers/_genome-sync.ts +297 -0
- package/servers/_history-tracker.ts +279 -0
- package/servers/_http-helpers.ts +151 -0
- package/servers/_identity-hash.ts +151 -0
- package/servers/_llm-providers/anthropic.ts +132 -0
- package/servers/_llm-providers/cloud.ts +195 -0
- package/servers/_llm-providers/index.ts +350 -0
- package/servers/_llm-providers/local.ts +127 -0
- package/servers/_llm-providers/onnx.ts +393 -0
- package/servers/_llm-providers/types.ts +27 -0
- package/servers/_manifest-v2.ts +447 -0
- package/servers/_mcp-host.ts +93 -0
- package/servers/_nudge-events.ts +451 -0
- package/servers/_orchestrate-executor.ts +316 -0
- package/servers/_prefetch.ts +435 -0
- package/servers/_pricing.ts +256 -0
- package/servers/_pro.ts +381 -0
- package/servers/_read-cache.ts +44 -0
- package/servers/_router-handlers.ts +46 -0
- package/servers/_router.ts +55 -0
- package/servers/_run-with-timeout.ts +173 -0
- package/servers/_savings-render.ts +214 -0
- package/servers/_stats-sqlite.ts +640 -0
- package/servers/_stats.ts +1183 -0
- package/servers/_status-line-cells.ts +469 -0
- package/servers/_streaks.ts +162 -0
- package/servers/_summarize.ts +208 -0
- package/servers/_task-graph-render.ts +217 -0
- package/servers/_task-graph.ts +545 -0
- package/servers/_telemetry.ts +719 -0
- package/servers/_test-parsers.ts +530 -0
- package/servers/_test-watch.ts +626 -0
- package/servers/_text-helpers.ts +37 -0
- package/servers/_token-measure.ts +160 -0
- package/servers/_tool-base.ts +248 -0
- package/servers/_weekly-digest.ts +391 -0
- package/servers/ask-server-handlers.ts +49 -0
- package/servers/ask-server.ts +174 -0
- package/servers/bash-server-handlers.ts +138 -0
- package/servers/bash-server.ts +1136 -0
- package/servers/bench.ts +186 -0
- package/servers/diff-semantic-server-handlers.ts +39 -0
- package/servers/diff-semantic-server.ts +566 -0
- package/servers/diff-server-handlers.ts +317 -0
- package/servers/diff-server.ts +20 -0
- package/servers/edit-server-handlers.ts +31 -0
- package/servers/edit-server.ts +209 -0
- package/servers/edit-structural-server-handlers.ts +322 -0
- package/servers/efficiency-server-handlers.ts +161 -0
- package/servers/efficiency-server.ts +1135 -0
- package/servers/flush-server-handlers.ts +21 -0
- package/servers/flush-server.ts +19 -0
- package/servers/genome-server-handlers.ts +118 -0
- package/servers/genome-server.ts +397 -0
- package/servers/github-server-handlers.ts +187 -0
- package/servers/github-server.ts +856 -0
- package/servers/glob-server-handlers.ts +233 -0
- package/servers/glob-server.ts +20 -0
- package/servers/grep-server-handlers.ts +61 -0
- package/servers/grep-server.ts +564 -0
- package/servers/http-server-handlers.ts +36 -0
- package/servers/http-server.ts +149 -0
- package/servers/logs-server-handlers.ts +42 -0
- package/servers/logs-server.ts +359 -0
- package/servers/ls-server-handlers.ts +166 -0
- package/servers/ls-server.ts +20 -0
- package/servers/multi-edit-server-handlers.ts +51 -0
- package/servers/multi-edit-server.ts +422 -0
- package/servers/notebook-edit-server-handlers.ts +34 -0
- package/servers/notebook-edit-server.ts +181 -0
- package/servers/orient-server-handlers.ts +52 -0
- package/servers/orient-server.ts +436 -0
- package/servers/pipe-server-handlers.ts +68 -0
- package/servers/pipe-server.ts +300 -0
- package/servers/read-server-handlers.ts +42 -0
- package/servers/read-server.ts +206 -0
- package/servers/rename-file-server-handlers.ts +67 -0
- package/servers/rename-file-server.ts +699 -0
- package/servers/savings-server-handlers.ts +27 -0
- package/servers/savings-server.ts +54 -0
- package/servers/search-replace-regex-server-handlers.ts +96 -0
- package/servers/search-replace-regex-server.ts +751 -0
- package/servers/sql-server-handlers.ts +58 -0
- package/servers/sql-server.ts +505 -0
- package/servers/task-server-handlers.ts +358 -0
- package/servers/task-server.ts +24 -0
- package/servers/test-server-handlers.ts +351 -0
- package/servers/test-server.ts +20 -0
- package/servers/tree-server-handlers.ts +503 -0
- package/servers/tree-server.ts +20 -0
- package/servers/webfetch-server-handlers.ts +166 -0
- package/servers/webfetch-server.ts +20 -0
- package/servers/websearch-server-handlers.ts +215 -0
- package/servers/websearch-server.ts +19 -0
- package/servers/write-server-handlers.ts +27 -0
- package/servers/write-server.ts +97 -0
- package/skills/ashlr-brief/SKILL.md +80 -0
- package/skills/ashlr-codex-doctor/SKILL.md +21 -0
- package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
- package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
- package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
- package/skills/ashlr-codex-savings/SKILL.md +17 -0
- package/skills/ashlr-cost-refactor/SKILL.md +50 -0
- package/skills/ashlr-efficient/SKILL.md +64 -0
- package/skills/ashlr-genome-author/SKILL.md +43 -0
- package/skills/ashlr-lean-tools/SKILL.md +43 -0
- package/skills/ashlr-search/SKILL.md +46 -0
|
@@ -0,0 +1,2140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import (
|
|
4
|
+
TYPE_CHECKING,
|
|
5
|
+
Any,
|
|
6
|
+
Literal,
|
|
7
|
+
Self,
|
|
8
|
+
cast,
|
|
9
|
+
overload,
|
|
10
|
+
)
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from pandas._config import (
|
|
16
|
+
is_nan_na,
|
|
17
|
+
using_python_scalars,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from pandas._libs import (
|
|
21
|
+
algos as libalgos,
|
|
22
|
+
lib,
|
|
23
|
+
missing as libmissing,
|
|
24
|
+
)
|
|
25
|
+
from pandas._libs.tslibs import is_supported_dtype
|
|
26
|
+
from pandas.compat import (
|
|
27
|
+
IS64,
|
|
28
|
+
is_platform_windows,
|
|
29
|
+
)
|
|
30
|
+
from pandas.errors import (
|
|
31
|
+
AbstractMethodError,
|
|
32
|
+
Pandas4Warning,
|
|
33
|
+
)
|
|
34
|
+
from pandas.util._exceptions import find_stack_level
|
|
35
|
+
|
|
36
|
+
from pandas.core.dtypes.astype import astype_is_view
|
|
37
|
+
from pandas.core.dtypes.base import ExtensionDtype
|
|
38
|
+
from pandas.core.dtypes.cast import (
|
|
39
|
+
construct_1d_object_array_from_listlike,
|
|
40
|
+
maybe_downcast_to_dtype,
|
|
41
|
+
)
|
|
42
|
+
from pandas.core.dtypes.common import (
|
|
43
|
+
is_bool,
|
|
44
|
+
is_integer_dtype,
|
|
45
|
+
is_list_like,
|
|
46
|
+
is_scalar,
|
|
47
|
+
is_string_dtype,
|
|
48
|
+
pandas_dtype,
|
|
49
|
+
)
|
|
50
|
+
from pandas.core.dtypes.dtypes import (
|
|
51
|
+
ArrowDtype,
|
|
52
|
+
BaseMaskedDtype,
|
|
53
|
+
)
|
|
54
|
+
from pandas.core.dtypes.missing import (
|
|
55
|
+
array_equivalent,
|
|
56
|
+
is_valid_na_for_dtype,
|
|
57
|
+
isna,
|
|
58
|
+
notna,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from pandas.core import (
|
|
62
|
+
algorithms as algos,
|
|
63
|
+
arraylike,
|
|
64
|
+
missing,
|
|
65
|
+
nanops,
|
|
66
|
+
ops,
|
|
67
|
+
)
|
|
68
|
+
from pandas.core.algorithms import (
|
|
69
|
+
factorize_array,
|
|
70
|
+
isin,
|
|
71
|
+
mode,
|
|
72
|
+
take,
|
|
73
|
+
)
|
|
74
|
+
from pandas.core.array_algos import (
|
|
75
|
+
masked_accumulations,
|
|
76
|
+
masked_reductions,
|
|
77
|
+
)
|
|
78
|
+
from pandas.core.array_algos.quantile import quantile_with_mask
|
|
79
|
+
from pandas.core.array_algos.transforms import shift
|
|
80
|
+
from pandas.core.arraylike import OpsMixin
|
|
81
|
+
from pandas.core.arrays._utils import to_numpy_dtype_inference
|
|
82
|
+
from pandas.core.arrays.base import ExtensionArray
|
|
83
|
+
from pandas.core.construction import (
|
|
84
|
+
array as pd_array,
|
|
85
|
+
ensure_wrapped_if_datetimelike,
|
|
86
|
+
extract_array,
|
|
87
|
+
)
|
|
88
|
+
from pandas.core.indexers import (
|
|
89
|
+
check_array_indexer,
|
|
90
|
+
getitem_returns_view,
|
|
91
|
+
)
|
|
92
|
+
from pandas.core.ops import invalid_comparison
|
|
93
|
+
from pandas.core.util.hashing import hash_array
|
|
94
|
+
|
|
95
|
+
if TYPE_CHECKING:
|
|
96
|
+
from collections.abc import Callable
|
|
97
|
+
from collections.abc import (
|
|
98
|
+
Iterator,
|
|
99
|
+
Sequence,
|
|
100
|
+
)
|
|
101
|
+
from pandas import Series
|
|
102
|
+
from pandas.core.arrays import BooleanArray
|
|
103
|
+
from pandas._typing import (
|
|
104
|
+
Dtype,
|
|
105
|
+
NumpySorter,
|
|
106
|
+
NumpyValueArrayLike,
|
|
107
|
+
ArrayLike,
|
|
108
|
+
AstypeArg,
|
|
109
|
+
AxisInt,
|
|
110
|
+
DtypeObj,
|
|
111
|
+
FillnaOptions,
|
|
112
|
+
InterpolateOptions,
|
|
113
|
+
NpDtype,
|
|
114
|
+
PositionalIndexer,
|
|
115
|
+
Scalar,
|
|
116
|
+
ScalarIndexer,
|
|
117
|
+
SequenceIndexer,
|
|
118
|
+
Shape,
|
|
119
|
+
npt,
|
|
120
|
+
)
|
|
121
|
+
from pandas._libs.missing import NAType
|
|
122
|
+
from pandas.core.arrays import FloatingArray
|
|
123
|
+
|
|
124
|
+
from pandas.compat.numpy import function as nv
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class BaseMaskedArray(OpsMixin, ExtensionArray):
|
|
128
|
+
"""
|
|
129
|
+
Base class for masked arrays (which use _data and _mask to store the data).
|
|
130
|
+
|
|
131
|
+
numpy based
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
# our underlying data and mask are each ndarrays
|
|
135
|
+
_data: np.ndarray
|
|
136
|
+
_mask: npt.NDArray[np.bool_]
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def _simple_new(cls, values: np.ndarray, mask: npt.NDArray[np.bool_]) -> Self:
|
|
140
|
+
result = BaseMaskedArray.__new__(cls)
|
|
141
|
+
result._data = values
|
|
142
|
+
result._mask = mask
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
def __init__(
|
|
146
|
+
self, values: np.ndarray, mask: npt.NDArray[np.bool_], copy: bool = False
|
|
147
|
+
) -> None:
|
|
148
|
+
# values is supposed to already be validated in the subclass
|
|
149
|
+
if not (isinstance(mask, np.ndarray) and mask.dtype == np.bool_):
|
|
150
|
+
raise TypeError(
|
|
151
|
+
"mask should be boolean numpy array. Use "
|
|
152
|
+
"the 'pd.array' function instead"
|
|
153
|
+
)
|
|
154
|
+
if values.shape != mask.shape:
|
|
155
|
+
raise ValueError("values.shape must match mask.shape")
|
|
156
|
+
|
|
157
|
+
if copy:
|
|
158
|
+
values = values.copy()
|
|
159
|
+
mask = mask.copy()
|
|
160
|
+
|
|
161
|
+
self._data = values
|
|
162
|
+
self._mask = mask
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def _from_sequence(cls, scalars, *, dtype=None, copy: bool = False) -> Self:
|
|
166
|
+
values, mask = cls._coerce_to_array(scalars, dtype=dtype, copy=copy)
|
|
167
|
+
return cls(values, mask)
|
|
168
|
+
|
|
169
|
+
def _cast_pointwise_result(self, values) -> ArrayLike:
|
|
170
|
+
if isna(values).all():
|
|
171
|
+
return type(self)._from_sequence(values, dtype=self.dtype)
|
|
172
|
+
if not (isinstance(values, np.ndarray) and values.dtype == object):
|
|
173
|
+
values = construct_1d_object_array_from_listlike(values)
|
|
174
|
+
result = lib.maybe_convert_objects(values, convert_to_nullable_dtype=True)
|
|
175
|
+
lkind = self.dtype.kind
|
|
176
|
+
rkind = result.dtype.kind
|
|
177
|
+
if (lkind in "iu" and rkind in "iu") or (lkind == rkind == "f"):
|
|
178
|
+
result = cast("BaseMaskedArray", result)
|
|
179
|
+
new_data = maybe_downcast_to_dtype(
|
|
180
|
+
result._data, dtype=self.dtype.numpy_dtype
|
|
181
|
+
)
|
|
182
|
+
result = type(result)(new_data, result._mask)
|
|
183
|
+
return result
|
|
184
|
+
|
|
185
|
+
@classmethod
|
|
186
|
+
def _empty(cls, shape: Shape, dtype: ExtensionDtype) -> Self:
|
|
187
|
+
"""
|
|
188
|
+
Create an ExtensionArray with the given shape and dtype.
|
|
189
|
+
|
|
190
|
+
See also
|
|
191
|
+
--------
|
|
192
|
+
ExtensionDtype.empty
|
|
193
|
+
ExtensionDtype.empty is the 'official' public version of this API.
|
|
194
|
+
"""
|
|
195
|
+
dtype = cast("BaseMaskedDtype", dtype)
|
|
196
|
+
values: np.ndarray = np.empty(shape, dtype=dtype.type)
|
|
197
|
+
values.fill(dtype._internal_fill_value)
|
|
198
|
+
mask = np.ones(shape, dtype=bool)
|
|
199
|
+
result = cls(values, mask)
|
|
200
|
+
if not isinstance(result, cls) or dtype != result.dtype:
|
|
201
|
+
raise NotImplementedError(
|
|
202
|
+
f"Default 'empty' implementation is invalid for dtype='{dtype}'"
|
|
203
|
+
)
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
def _formatter(self, boxed: bool = False) -> Callable[[Any], str | None]:
|
|
207
|
+
# NEP 51: https://github.com/numpy/numpy/pull/22449
|
|
208
|
+
return str
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def dtype(self) -> BaseMaskedDtype:
|
|
212
|
+
raise AbstractMethodError(self)
|
|
213
|
+
|
|
214
|
+
@overload
|
|
215
|
+
def __getitem__(self, item: ScalarIndexer) -> Any: ...
|
|
216
|
+
|
|
217
|
+
@overload
|
|
218
|
+
def __getitem__(self, item: SequenceIndexer) -> Self: ...
|
|
219
|
+
|
|
220
|
+
def __getitem__(self, item: PositionalIndexer) -> Self | Any:
|
|
221
|
+
item = check_array_indexer(self, item)
|
|
222
|
+
|
|
223
|
+
newmask = self._mask[item]
|
|
224
|
+
if is_bool(newmask):
|
|
225
|
+
# This is a scalar indexing
|
|
226
|
+
if newmask:
|
|
227
|
+
return self.dtype.na_value
|
|
228
|
+
return self._data[item]
|
|
229
|
+
|
|
230
|
+
result = self._simple_new(self._data[item], newmask)
|
|
231
|
+
if getitem_returns_view(self, item):
|
|
232
|
+
result._readonly = self._readonly
|
|
233
|
+
return result
|
|
234
|
+
|
|
235
|
+
def _pad_or_backfill(
|
|
236
|
+
self,
|
|
237
|
+
*,
|
|
238
|
+
method: FillnaOptions,
|
|
239
|
+
limit: int | None = None,
|
|
240
|
+
limit_area: Literal["inside", "outside"] | None = None,
|
|
241
|
+
copy: bool = True,
|
|
242
|
+
) -> Self:
|
|
243
|
+
mask = self._mask
|
|
244
|
+
|
|
245
|
+
if mask.any():
|
|
246
|
+
func = missing.get_fill_func(method, ndim=self.ndim)
|
|
247
|
+
|
|
248
|
+
npvalues = self._data.T
|
|
249
|
+
new_mask = mask.T
|
|
250
|
+
if copy:
|
|
251
|
+
npvalues = npvalues.copy()
|
|
252
|
+
new_mask = new_mask.copy()
|
|
253
|
+
elif limit_area is not None:
|
|
254
|
+
mask = mask.copy()
|
|
255
|
+
func(npvalues, limit=limit, mask=new_mask)
|
|
256
|
+
|
|
257
|
+
if limit_area is not None and not mask.all():
|
|
258
|
+
mask = mask.T
|
|
259
|
+
neg_mask = ~mask
|
|
260
|
+
first = neg_mask.argmax()
|
|
261
|
+
last = len(neg_mask) - neg_mask[::-1].argmax() - 1
|
|
262
|
+
if limit_area == "inside":
|
|
263
|
+
new_mask[:first] |= mask[:first]
|
|
264
|
+
new_mask[last + 1 :] |= mask[last + 1 :]
|
|
265
|
+
elif limit_area == "outside":
|
|
266
|
+
new_mask[first + 1 : last] |= mask[first + 1 : last]
|
|
267
|
+
|
|
268
|
+
if copy:
|
|
269
|
+
return self._simple_new(npvalues.T, new_mask.T)
|
|
270
|
+
else:
|
|
271
|
+
return self
|
|
272
|
+
elif copy:
|
|
273
|
+
new_values = self.copy()
|
|
274
|
+
else:
|
|
275
|
+
new_values = self
|
|
276
|
+
return new_values
|
|
277
|
+
|
|
278
|
+
def fillna(self, value, limit: int | None = None, copy: bool = True) -> Self:
|
|
279
|
+
"""
|
|
280
|
+
Fill NA/NaN values using the specified method.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
value : scalar, array-like
|
|
285
|
+
If a scalar value is passed it is used to fill all missing values.
|
|
286
|
+
Alternatively, an array-like "value" can be given. It's expected
|
|
287
|
+
that the array-like have the same length as 'self'.
|
|
288
|
+
limit : int, default None
|
|
289
|
+
The maximum number of entries where NA values will be filled.
|
|
290
|
+
copy : bool, default True
|
|
291
|
+
Whether to make a copy of the data before filling. If False, then
|
|
292
|
+
the original should be modified and no new memory should be allocated.
|
|
293
|
+
For ExtensionArray subclasses that cannot do this, it is at the
|
|
294
|
+
author's discretion whether to ignore "copy=False" or to raise.
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
ExtensionArray
|
|
299
|
+
With NA/NaN filled.
|
|
300
|
+
|
|
301
|
+
See Also
|
|
302
|
+
--------
|
|
303
|
+
api.extensions.ExtensionArray.dropna : Return ExtensionArray without
|
|
304
|
+
NA values.
|
|
305
|
+
api.extensions.ExtensionArray.isna : A 1-D array indicating if
|
|
306
|
+
each value is missing.
|
|
307
|
+
|
|
308
|
+
Examples
|
|
309
|
+
--------
|
|
310
|
+
>>> arr = pd.array([np.nan, np.nan, 2, 3, np.nan, np.nan])
|
|
311
|
+
>>> arr.fillna(0)
|
|
312
|
+
<IntegerArray>
|
|
313
|
+
[0, 0, 2, 3, 0, 0]
|
|
314
|
+
Length: 6, dtype: Int64
|
|
315
|
+
"""
|
|
316
|
+
mask = self._mask
|
|
317
|
+
|
|
318
|
+
# Fast path: single-pass Cython for scalar fill values. GH#42147
|
|
319
|
+
# Skip when fill value is itself NA — filling NAs with NA is a no-op,
|
|
320
|
+
# and the Cython path would incorrectly clear mask bits.
|
|
321
|
+
# When NaN and NA are distinguished (is_nan_na() is False), float NaN
|
|
322
|
+
# is a valid fill value, not NA.
|
|
323
|
+
value_is_na = is_valid_na_for_dtype(value, self.dtype) and not (
|
|
324
|
+
lib.is_float(value) and not is_nan_na()
|
|
325
|
+
)
|
|
326
|
+
if lib.is_scalar(value) and not value_is_na:
|
|
327
|
+
if not mask.any():
|
|
328
|
+
return self.copy() if copy else self[:]
|
|
329
|
+
try:
|
|
330
|
+
casted = self._validate_setitem_value(value)
|
|
331
|
+
except TypeError:
|
|
332
|
+
pass
|
|
333
|
+
else:
|
|
334
|
+
if copy:
|
|
335
|
+
new_data = self._data.copy()
|
|
336
|
+
new_mask = mask.copy()
|
|
337
|
+
else:
|
|
338
|
+
if self._readonly:
|
|
339
|
+
raise ValueError("Cannot modify read-only array")
|
|
340
|
+
new_data = self._data
|
|
341
|
+
new_mask = mask
|
|
342
|
+
|
|
343
|
+
if new_data.dtype.kind == "b":
|
|
344
|
+
# bool arrays need uint8 view for Cython memoryview
|
|
345
|
+
libalgos.scalar_fillna_inplace(
|
|
346
|
+
new_data.view("u1"),
|
|
347
|
+
np.uint8(casted),
|
|
348
|
+
mask=new_mask.view("u1"),
|
|
349
|
+
limit=limit,
|
|
350
|
+
)
|
|
351
|
+
else:
|
|
352
|
+
libalgos.scalar_fillna_inplace(
|
|
353
|
+
new_data,
|
|
354
|
+
casted,
|
|
355
|
+
mask=new_mask.view("u1"),
|
|
356
|
+
limit=limit,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return type(self)(new_data, new_mask, copy=False)
|
|
360
|
+
|
|
361
|
+
if limit is not None and limit < len(self):
|
|
362
|
+
modify = mask.cumsum() > limit
|
|
363
|
+
if modify.any():
|
|
364
|
+
# Only copy mask if necessary
|
|
365
|
+
mask = mask.copy()
|
|
366
|
+
mask[modify] = False
|
|
367
|
+
|
|
368
|
+
value = missing.check_value_size(value, mask, len(self))
|
|
369
|
+
|
|
370
|
+
if mask.any():
|
|
371
|
+
# fill with value
|
|
372
|
+
if copy:
|
|
373
|
+
new_values = self.copy()
|
|
374
|
+
else:
|
|
375
|
+
new_values = self[:]
|
|
376
|
+
new_values[mask] = value
|
|
377
|
+
elif copy:
|
|
378
|
+
new_values = self.copy()
|
|
379
|
+
else:
|
|
380
|
+
new_values = self[:]
|
|
381
|
+
return new_values
|
|
382
|
+
|
|
383
|
+
@classmethod
|
|
384
|
+
def _coerce_to_array(
|
|
385
|
+
cls, values, *, dtype: DtypeObj, copy: bool = False
|
|
386
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
387
|
+
raise AbstractMethodError(cls)
|
|
388
|
+
|
|
389
|
+
def _validate_setitem_value(self, value):
|
|
390
|
+
"""
|
|
391
|
+
Check if we have a scalar that we can cast losslessly.
|
|
392
|
+
|
|
393
|
+
Raises
|
|
394
|
+
------
|
|
395
|
+
TypeError
|
|
396
|
+
"""
|
|
397
|
+
kind = self.dtype.kind
|
|
398
|
+
# TODO: get this all from np_can_hold_element?
|
|
399
|
+
if kind == "b":
|
|
400
|
+
if lib.is_bool(value):
|
|
401
|
+
return value
|
|
402
|
+
|
|
403
|
+
elif kind == "f":
|
|
404
|
+
if lib.is_integer(value) or lib.is_float(value):
|
|
405
|
+
return value
|
|
406
|
+
|
|
407
|
+
elif lib.is_integer(value) or (lib.is_float(value) and value.is_integer()):
|
|
408
|
+
return value
|
|
409
|
+
# TODO: unsigned checks
|
|
410
|
+
|
|
411
|
+
# Note: without the "str" here, the f-string rendering raises in
|
|
412
|
+
# py38 builds.
|
|
413
|
+
raise TypeError(f"Invalid value '{value!s}' for dtype '{self.dtype}'")
|
|
414
|
+
|
|
415
|
+
def insert(self, loc: int, item) -> Self:
|
|
416
|
+
if not is_valid_na_for_dtype(item, self.dtype):
|
|
417
|
+
self._validate_setitem_value(item)
|
|
418
|
+
return super().insert(loc, item)
|
|
419
|
+
|
|
420
|
+
def __setitem__(self, key, value) -> None:
|
|
421
|
+
if self._readonly:
|
|
422
|
+
raise ValueError("Cannot modify read-only array")
|
|
423
|
+
|
|
424
|
+
key = check_array_indexer(self, key)
|
|
425
|
+
|
|
426
|
+
if is_scalar(value):
|
|
427
|
+
if is_valid_na_for_dtype(value, self.dtype) and not (
|
|
428
|
+
lib.is_float(value) and not is_nan_na()
|
|
429
|
+
):
|
|
430
|
+
self._mask[key] = True
|
|
431
|
+
else:
|
|
432
|
+
value = self._validate_setitem_value(value)
|
|
433
|
+
self._data[key] = value
|
|
434
|
+
self._mask[key] = False
|
|
435
|
+
return
|
|
436
|
+
|
|
437
|
+
value, mask = self._coerce_to_array(value, dtype=self.dtype)
|
|
438
|
+
|
|
439
|
+
self._data[key] = value
|
|
440
|
+
self._mask[key] = mask
|
|
441
|
+
|
|
442
|
+
def __contains__(self, key) -> bool:
|
|
443
|
+
if isna(key) and key is not self.dtype.na_value:
|
|
444
|
+
# GH#52840
|
|
445
|
+
if lib.is_float(key) and is_nan_na():
|
|
446
|
+
key = self.dtype.na_value
|
|
447
|
+
elif self._data.dtype.kind == "f" and lib.is_float(key):
|
|
448
|
+
return bool((np.isnan(self._data) & ~self._mask).any())
|
|
449
|
+
|
|
450
|
+
return bool(super().__contains__(key))
|
|
451
|
+
|
|
452
|
+
def __iter__(self) -> Iterator:
|
|
453
|
+
if self.ndim == 1:
|
|
454
|
+
if not self._hasna:
|
|
455
|
+
for val in self._data:
|
|
456
|
+
yield val
|
|
457
|
+
else:
|
|
458
|
+
na_value = self.dtype.na_value
|
|
459
|
+
for isna_, val in zip(self._mask, self._data, strict=True):
|
|
460
|
+
if isna_:
|
|
461
|
+
yield na_value
|
|
462
|
+
else:
|
|
463
|
+
yield val
|
|
464
|
+
else:
|
|
465
|
+
for i in range(len(self)):
|
|
466
|
+
yield self[i]
|
|
467
|
+
|
|
468
|
+
def __len__(self) -> int:
|
|
469
|
+
return len(self._data)
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def shape(self) -> Shape:
|
|
473
|
+
return self._data.shape
|
|
474
|
+
|
|
475
|
+
@property
|
|
476
|
+
def ndim(self) -> int:
|
|
477
|
+
return self._data.ndim
|
|
478
|
+
|
|
479
|
+
def swapaxes(self, axis1, axis2) -> Self:
|
|
480
|
+
data = self._data.swapaxes(axis1, axis2)
|
|
481
|
+
mask = self._mask.swapaxes(axis1, axis2)
|
|
482
|
+
return self._simple_new(data, mask)
|
|
483
|
+
|
|
484
|
+
def delete(self, loc, axis: AxisInt = 0) -> Self:
|
|
485
|
+
data = np.delete(self._data, loc, axis=axis)
|
|
486
|
+
mask = np.delete(self._mask, loc, axis=axis)
|
|
487
|
+
return self._simple_new(data, mask)
|
|
488
|
+
|
|
489
|
+
def reshape(self, *args, **kwargs) -> Self:
|
|
490
|
+
data = self._data.reshape(*args, **kwargs)
|
|
491
|
+
mask = self._mask.reshape(*args, **kwargs)
|
|
492
|
+
return self._simple_new(data, mask)
|
|
493
|
+
|
|
494
|
+
def ravel(self, *args, **kwargs) -> Self:
|
|
495
|
+
# TODO: need to make sure we have the same order for data/mask
|
|
496
|
+
data = self._data.ravel(*args, **kwargs)
|
|
497
|
+
mask = self._mask.ravel(*args, **kwargs)
|
|
498
|
+
return type(self)(data, mask)
|
|
499
|
+
|
|
500
|
+
def shift(self, periods: int = 1, fill_value=None) -> Self:
|
|
501
|
+
# NB: shift is always along axis=self.ndim-1
|
|
502
|
+
if fill_value is None:
|
|
503
|
+
new_data = shift(self._data, periods, 0)
|
|
504
|
+
new_mask = shift(self._mask, periods, True)
|
|
505
|
+
else:
|
|
506
|
+
new_data = shift(self._data, periods, fill_value)
|
|
507
|
+
new_mask = shift(self._mask, periods, False)
|
|
508
|
+
return type(self)(new_data, new_mask)
|
|
509
|
+
|
|
510
|
+
@property
|
|
511
|
+
def T(self) -> Self:
|
|
512
|
+
return self._simple_new(self._data.T, self._mask.T)
|
|
513
|
+
|
|
514
|
+
def round(self, decimals: int = 0, *args, **kwargs):
|
|
515
|
+
"""
|
|
516
|
+
Round each value in the array a to the given number of decimals.
|
|
517
|
+
|
|
518
|
+
Parameters
|
|
519
|
+
----------
|
|
520
|
+
decimals : int, default 0
|
|
521
|
+
Number of decimal places to round to. If decimals is negative,
|
|
522
|
+
it specifies the number of positions to the left of the decimal point.
|
|
523
|
+
*args, **kwargs
|
|
524
|
+
Additional arguments and keywords have no effect but might be
|
|
525
|
+
accepted for compatibility with NumPy.
|
|
526
|
+
|
|
527
|
+
Returns
|
|
528
|
+
-------
|
|
529
|
+
NumericArray
|
|
530
|
+
Rounded values of the NumericArray.
|
|
531
|
+
|
|
532
|
+
See Also
|
|
533
|
+
--------
|
|
534
|
+
numpy.around : Round values of an np.array.
|
|
535
|
+
DataFrame.round : Round values of a DataFrame.
|
|
536
|
+
Series.round : Round values of a Series.
|
|
537
|
+
"""
|
|
538
|
+
if self.dtype.kind == "b":
|
|
539
|
+
return self
|
|
540
|
+
nv.validate_round(args, kwargs)
|
|
541
|
+
values = np.round(self._data, decimals=decimals, **kwargs)
|
|
542
|
+
|
|
543
|
+
# Usually we'll get same type as self, but ndarray[bool] casts to float
|
|
544
|
+
return self._maybe_mask_result(values, self._mask.copy())
|
|
545
|
+
|
|
546
|
+
# ------------------------------------------------------------------
|
|
547
|
+
# Unary Methods
|
|
548
|
+
|
|
549
|
+
def __invert__(self) -> Self:
|
|
550
|
+
return self._simple_new(~self._data, self._mask.copy())
|
|
551
|
+
|
|
552
|
+
def __neg__(self) -> Self:
|
|
553
|
+
return self._simple_new(-self._data, self._mask.copy())
|
|
554
|
+
|
|
555
|
+
def __pos__(self) -> Self:
|
|
556
|
+
return self.copy()
|
|
557
|
+
|
|
558
|
+
def __abs__(self) -> Self:
|
|
559
|
+
return self._simple_new(abs(self._data), self._mask.copy())
|
|
560
|
+
|
|
561
|
+
# ------------------------------------------------------------------
|
|
562
|
+
|
|
563
|
+
def _values_for_json(self) -> np.ndarray:
|
|
564
|
+
return np.asarray(self, dtype=object)
|
|
565
|
+
|
|
566
|
+
def to_numpy(
|
|
567
|
+
self,
|
|
568
|
+
dtype: npt.DTypeLike | None = None,
|
|
569
|
+
copy: bool = False,
|
|
570
|
+
na_value: object = lib.no_default,
|
|
571
|
+
) -> np.ndarray:
|
|
572
|
+
"""
|
|
573
|
+
Convert to a NumPy Array.
|
|
574
|
+
|
|
575
|
+
By default converts to an object-dtype NumPy array. Specify the `dtype` and
|
|
576
|
+
`na_value` keywords to customize the conversion.
|
|
577
|
+
|
|
578
|
+
Parameters
|
|
579
|
+
----------
|
|
580
|
+
dtype : dtype, default object
|
|
581
|
+
The numpy dtype to convert to.
|
|
582
|
+
copy : bool, default False
|
|
583
|
+
Whether to ensure that the returned value is a not a view on
|
|
584
|
+
the array. Note that ``copy=False`` does not *ensure* that
|
|
585
|
+
``to_numpy()`` is no-copy. Rather, ``copy=True`` ensure that
|
|
586
|
+
a copy is made, even if not strictly necessary. This is typically
|
|
587
|
+
only possible when no missing values are present and `dtype`
|
|
588
|
+
is the equivalent numpy dtype.
|
|
589
|
+
na_value : scalar, optional
|
|
590
|
+
Scalar missing value indicator to use in numpy array. Defaults
|
|
591
|
+
to the native missing value indicator of this array (pd.NA).
|
|
592
|
+
|
|
593
|
+
Returns
|
|
594
|
+
-------
|
|
595
|
+
numpy.ndarray
|
|
596
|
+
|
|
597
|
+
Examples
|
|
598
|
+
--------
|
|
599
|
+
An object-dtype is the default result
|
|
600
|
+
|
|
601
|
+
>>> a = pd.array([True, False, pd.NA], dtype="boolean")
|
|
602
|
+
>>> a.to_numpy()
|
|
603
|
+
array([True, False, <NA>], dtype=object)
|
|
604
|
+
|
|
605
|
+
When no missing values are present, an equivalent dtype can be used.
|
|
606
|
+
|
|
607
|
+
>>> pd.array([True, False], dtype="boolean").to_numpy(dtype="bool")
|
|
608
|
+
array([ True, False])
|
|
609
|
+
>>> pd.array([1, 2], dtype="Int64").to_numpy("int64")
|
|
610
|
+
array([1, 2])
|
|
611
|
+
|
|
612
|
+
However, requesting such dtype will raise a ValueError if
|
|
613
|
+
missing values are present and the default missing value :attr:`NA`
|
|
614
|
+
is used.
|
|
615
|
+
|
|
616
|
+
>>> a = pd.array([True, False, pd.NA], dtype="boolean")
|
|
617
|
+
>>> a
|
|
618
|
+
<BooleanArray>
|
|
619
|
+
[True, False, <NA>]
|
|
620
|
+
Length: 3, dtype: boolean
|
|
621
|
+
|
|
622
|
+
>>> a.to_numpy(dtype="bool")
|
|
623
|
+
Traceback (most recent call last):
|
|
624
|
+
...
|
|
625
|
+
ValueError: cannot convert to bool numpy array in presence of missing values
|
|
626
|
+
|
|
627
|
+
Specify a valid `na_value` instead
|
|
628
|
+
|
|
629
|
+
>>> a.to_numpy(dtype="bool", na_value=False)
|
|
630
|
+
array([ True, False, False])
|
|
631
|
+
"""
|
|
632
|
+
hasna = self._hasna
|
|
633
|
+
dtype, na_value = to_numpy_dtype_inference(self, dtype, na_value, hasna)
|
|
634
|
+
if dtype is None:
|
|
635
|
+
dtype = np.dtype(object)
|
|
636
|
+
|
|
637
|
+
if hasna:
|
|
638
|
+
if (
|
|
639
|
+
dtype != np.dtype(object)
|
|
640
|
+
and not is_string_dtype(dtype)
|
|
641
|
+
and na_value is libmissing.NA
|
|
642
|
+
):
|
|
643
|
+
raise ValueError(
|
|
644
|
+
f"cannot convert to '{dtype}'-dtype NumPy array "
|
|
645
|
+
"with missing values. Specify an appropriate 'na_value' "
|
|
646
|
+
"for this dtype."
|
|
647
|
+
)
|
|
648
|
+
# don't pass copy to astype -> always need a copy since we are mutating
|
|
649
|
+
with warnings.catch_warnings():
|
|
650
|
+
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
|
651
|
+
data = self._data.astype(dtype)
|
|
652
|
+
data[self._mask] = na_value
|
|
653
|
+
else:
|
|
654
|
+
with warnings.catch_warnings():
|
|
655
|
+
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
|
656
|
+
data = self._data.astype(dtype, copy=copy)
|
|
657
|
+
if self._readonly and not copy and astype_is_view(self.dtype, dtype):
|
|
658
|
+
data = data.view()
|
|
659
|
+
data.flags.writeable = False
|
|
660
|
+
return data
|
|
661
|
+
|
|
662
|
+
def tolist(self) -> list:
|
|
663
|
+
"""
|
|
664
|
+
Return a list of the values.
|
|
665
|
+
|
|
666
|
+
These are each a scalar type, which is a Python scalar
|
|
667
|
+
(for str, int, float) or a pandas scalar
|
|
668
|
+
(for Timestamp/Timedelta/Interval/Period)
|
|
669
|
+
|
|
670
|
+
Returns
|
|
671
|
+
-------
|
|
672
|
+
list
|
|
673
|
+
Python list of values in array.
|
|
674
|
+
|
|
675
|
+
See Also
|
|
676
|
+
--------
|
|
677
|
+
Index.to_list: Return a list of the values in the Index.
|
|
678
|
+
Series.to_list: Return a list of the values in the Series.
|
|
679
|
+
|
|
680
|
+
Examples
|
|
681
|
+
--------
|
|
682
|
+
>>> arr = pd.array([1, 2, 3])
|
|
683
|
+
>>> arr.tolist()
|
|
684
|
+
[1, 2, 3]
|
|
685
|
+
"""
|
|
686
|
+
if self.ndim > 1:
|
|
687
|
+
return [x.tolist() for x in self]
|
|
688
|
+
dtype = None if self._hasna else self._data.dtype
|
|
689
|
+
return self.to_numpy(dtype=dtype, na_value=libmissing.NA).tolist()
|
|
690
|
+
|
|
691
|
+
@overload
|
|
692
|
+
def astype(self, dtype: npt.DTypeLike, copy: bool = ...) -> np.ndarray: ...
|
|
693
|
+
|
|
694
|
+
@overload
|
|
695
|
+
def astype(self, dtype: ExtensionDtype, copy: bool = ...) -> ExtensionArray: ...
|
|
696
|
+
|
|
697
|
+
@overload
|
|
698
|
+
def astype(self, dtype: AstypeArg, copy: bool = ...) -> ArrayLike: ...
|
|
699
|
+
|
|
700
|
+
def astype(self, dtype: AstypeArg, copy: bool = True) -> ArrayLike:
|
|
701
|
+
dtype = pandas_dtype(dtype)
|
|
702
|
+
|
|
703
|
+
if dtype == self.dtype:
|
|
704
|
+
if copy:
|
|
705
|
+
return self.copy()
|
|
706
|
+
return self
|
|
707
|
+
|
|
708
|
+
# if we are astyping to another nullable masked dtype, we can fastpath
|
|
709
|
+
if isinstance(dtype, BaseMaskedDtype):
|
|
710
|
+
# TODO deal with NaNs for FloatingArray case
|
|
711
|
+
with warnings.catch_warnings():
|
|
712
|
+
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
|
713
|
+
# TODO: Is rounding what we want long term?
|
|
714
|
+
data = self._data.astype(dtype.numpy_dtype, copy=copy)
|
|
715
|
+
# mask is copied depending on whether the data was copied, and
|
|
716
|
+
# not directly depending on the `copy` keyword
|
|
717
|
+
mask = self._mask if data is self._data else self._mask.copy()
|
|
718
|
+
cls = dtype.construct_array_type()
|
|
719
|
+
return cls(data, mask, copy=False)
|
|
720
|
+
|
|
721
|
+
if isinstance(dtype, ExtensionDtype):
|
|
722
|
+
eacls = dtype.construct_array_type()
|
|
723
|
+
return eacls._from_sequence(self, dtype=dtype, copy=copy)
|
|
724
|
+
|
|
725
|
+
na_value: float | np.datetime64 | lib.NoDefault
|
|
726
|
+
|
|
727
|
+
# coerce
|
|
728
|
+
if dtype.kind == "f":
|
|
729
|
+
# In astype, we consider dtype=float to also mean na_value=np.nan
|
|
730
|
+
na_value = np.nan
|
|
731
|
+
elif dtype.kind == "M":
|
|
732
|
+
unit = np.datetime_data(dtype)[0]
|
|
733
|
+
na_value = np.datetime64("NaT", unit) # type: ignore[call-overload]
|
|
734
|
+
else:
|
|
735
|
+
na_value = lib.no_default
|
|
736
|
+
|
|
737
|
+
# to_numpy will also raise, but we get somewhat nicer exception messages here
|
|
738
|
+
if dtype.kind in "iu" and self._hasna:
|
|
739
|
+
raise ValueError("cannot convert NA to integer")
|
|
740
|
+
if dtype.kind == "b" and self._hasna:
|
|
741
|
+
# careful: astype_nansafe converts np.nan to True
|
|
742
|
+
raise ValueError("cannot convert float NaN to bool")
|
|
743
|
+
|
|
744
|
+
data = self.to_numpy(dtype=dtype, na_value=na_value, copy=copy)
|
|
745
|
+
return data
|
|
746
|
+
|
|
747
|
+
__array_priority__ = 1000 # higher than ndarray so ops dispatch to us
|
|
748
|
+
|
|
749
|
+
def __array__(
|
|
750
|
+
self, dtype: NpDtype | None = None, copy: bool | None = None
|
|
751
|
+
) -> np.ndarray:
|
|
752
|
+
"""
|
|
753
|
+
the array interface, return my values
|
|
754
|
+
We return an object array here to preserve our scalar values
|
|
755
|
+
"""
|
|
756
|
+
if copy is False:
|
|
757
|
+
if not self._hasna:
|
|
758
|
+
# special case, here we can simply return the underlying data
|
|
759
|
+
result = np.array(self._data, dtype=dtype, copy=copy)
|
|
760
|
+
# If the ExtensionArray is readonly, make the numpy array readonly too
|
|
761
|
+
if self._readonly:
|
|
762
|
+
result = result.view()
|
|
763
|
+
result.flags.writeable = False
|
|
764
|
+
return result
|
|
765
|
+
raise ValueError(
|
|
766
|
+
"Unable to avoid copy while creating an array as requested."
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
if copy is None:
|
|
770
|
+
copy = False # The NumPy copy=False meaning is different here.
|
|
771
|
+
return self.to_numpy(dtype=dtype, copy=copy)
|
|
772
|
+
|
|
773
|
+
_HANDLED_TYPES: tuple[type, ...]
|
|
774
|
+
|
|
775
|
+
def __array_ufunc__(self, ufunc: np.ufunc, method: str, *inputs, **kwargs):
|
|
776
|
+
# For MaskedArray inputs, we apply the ufunc to ._data
|
|
777
|
+
# and mask the result.
|
|
778
|
+
|
|
779
|
+
out = kwargs.get("out", ())
|
|
780
|
+
|
|
781
|
+
for x in inputs + out:
|
|
782
|
+
if not isinstance(x, (*self._HANDLED_TYPES, BaseMaskedArray)):
|
|
783
|
+
return NotImplemented
|
|
784
|
+
|
|
785
|
+
# for binary ops, use our custom dunder methods
|
|
786
|
+
result = arraylike.maybe_dispatch_ufunc_to_dunder_op(
|
|
787
|
+
self, ufunc, method, *inputs, **kwargs
|
|
788
|
+
)
|
|
789
|
+
if result is not NotImplemented:
|
|
790
|
+
return result
|
|
791
|
+
|
|
792
|
+
if "out" in kwargs:
|
|
793
|
+
# e.g. test_ufunc_with_out
|
|
794
|
+
return arraylike.dispatch_ufunc_with_out(
|
|
795
|
+
self, ufunc, method, *inputs, **kwargs
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
if method == "reduce":
|
|
799
|
+
result = arraylike.dispatch_reduction_ufunc(
|
|
800
|
+
self, ufunc, method, *inputs, **kwargs
|
|
801
|
+
)
|
|
802
|
+
if result is not NotImplemented:
|
|
803
|
+
return result
|
|
804
|
+
|
|
805
|
+
mask = np.zeros(len(self), dtype=bool)
|
|
806
|
+
inputs2 = []
|
|
807
|
+
for x in inputs:
|
|
808
|
+
if isinstance(x, BaseMaskedArray):
|
|
809
|
+
mask |= x._mask
|
|
810
|
+
inputs2.append(x._data)
|
|
811
|
+
else:
|
|
812
|
+
inputs2.append(x)
|
|
813
|
+
|
|
814
|
+
def reconstruct(x: np.ndarray):
|
|
815
|
+
# we don't worry about scalar `x` here, since we
|
|
816
|
+
# raise for reduce up above.
|
|
817
|
+
from pandas.core.arrays import (
|
|
818
|
+
BooleanArray,
|
|
819
|
+
FloatingArray,
|
|
820
|
+
IntegerArray,
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
if x.dtype.kind == "b":
|
|
824
|
+
m = mask.copy()
|
|
825
|
+
return BooleanArray(x, m)
|
|
826
|
+
elif x.dtype.kind in "iu":
|
|
827
|
+
m = mask.copy()
|
|
828
|
+
return IntegerArray(x, m)
|
|
829
|
+
elif x.dtype.kind == "f":
|
|
830
|
+
m = mask.copy()
|
|
831
|
+
if x.dtype == np.float16:
|
|
832
|
+
# reached in e.g. np.sqrt on BooleanArray
|
|
833
|
+
# we don't support float16
|
|
834
|
+
x = x.astype(np.float32)
|
|
835
|
+
if is_nan_na():
|
|
836
|
+
m[np.isnan(x)] = True
|
|
837
|
+
return FloatingArray(x, m)
|
|
838
|
+
else:
|
|
839
|
+
x[mask] = np.nan
|
|
840
|
+
return x
|
|
841
|
+
|
|
842
|
+
result = getattr(ufunc, method)(*inputs2, **kwargs)
|
|
843
|
+
if ufunc.nout > 1:
|
|
844
|
+
# e.g. np.divmod
|
|
845
|
+
return tuple(reconstruct(x) for x in result)
|
|
846
|
+
elif method == "reduce":
|
|
847
|
+
# e.g. np.add.reduce; test_ufunc_reduce_raises
|
|
848
|
+
if self._mask.any():
|
|
849
|
+
return self._na_value
|
|
850
|
+
return result
|
|
851
|
+
else:
|
|
852
|
+
return reconstruct(result)
|
|
853
|
+
|
|
854
|
+
def __arrow_array__(self, type=None):
|
|
855
|
+
"""
|
|
856
|
+
Convert myself into a pyarrow Array.
|
|
857
|
+
"""
|
|
858
|
+
import pyarrow as pa
|
|
859
|
+
|
|
860
|
+
return pa.array(self._data, mask=self._mask, type=type)
|
|
861
|
+
|
|
862
|
+
@property
|
|
863
|
+
def _hasna(self) -> bool:
|
|
864
|
+
# Note: this is expensive right now! The hope is that we can
|
|
865
|
+
# make this faster by having an optional mask, but not have to change
|
|
866
|
+
# source code using it..
|
|
867
|
+
|
|
868
|
+
return bool(self._mask.any())
|
|
869
|
+
|
|
870
|
+
def _propagate_mask(
|
|
871
|
+
self, mask: npt.NDArray[np.bool_] | None, other
|
|
872
|
+
) -> npt.NDArray[np.bool_]:
|
|
873
|
+
if mask is None:
|
|
874
|
+
mask = self._mask.copy() # TODO: need test for BooleanArray needing a copy
|
|
875
|
+
if other is libmissing.NA:
|
|
876
|
+
# GH#45421 don't alter inplace
|
|
877
|
+
mask = mask | True
|
|
878
|
+
elif is_list_like(other) and len(other) == len(mask):
|
|
879
|
+
mask = mask | isna(other)
|
|
880
|
+
else:
|
|
881
|
+
mask = self._mask | mask
|
|
882
|
+
return mask
|
|
883
|
+
|
|
884
|
+
def _arith_method(self, other, op):
|
|
885
|
+
op_name = op.__name__
|
|
886
|
+
omask = None
|
|
887
|
+
|
|
888
|
+
if (
|
|
889
|
+
is_list_like(other)
|
|
890
|
+
and not isinstance(other, (list, np.ndarray, ExtensionArray))
|
|
891
|
+
and not ops.has_castable_attr(other)
|
|
892
|
+
):
|
|
893
|
+
warnings.warn(
|
|
894
|
+
f"Operation with {type(other).__name__} is deprecated. "
|
|
895
|
+
"In a future version these will be treated as scalar-like. "
|
|
896
|
+
"To retain the old behavior, explicitly wrap in a Series "
|
|
897
|
+
"instead.",
|
|
898
|
+
Pandas4Warning,
|
|
899
|
+
stacklevel=find_stack_level(),
|
|
900
|
+
)
|
|
901
|
+
|
|
902
|
+
if (
|
|
903
|
+
not hasattr(other, "dtype")
|
|
904
|
+
and is_list_like(other)
|
|
905
|
+
and len(other) == len(self)
|
|
906
|
+
):
|
|
907
|
+
# Try inferring masked dtype instead of casting to object
|
|
908
|
+
other = pd_array(other)
|
|
909
|
+
other = extract_array(other, extract_numpy=True)
|
|
910
|
+
|
|
911
|
+
if isinstance(other, BaseMaskedArray):
|
|
912
|
+
other, omask = other._data, other._mask
|
|
913
|
+
|
|
914
|
+
elif is_list_like(other):
|
|
915
|
+
if not isinstance(other, ExtensionArray):
|
|
916
|
+
other = np.asarray(other)
|
|
917
|
+
if other.ndim > 1:
|
|
918
|
+
raise NotImplementedError("can only perform ops with 1-d structures")
|
|
919
|
+
|
|
920
|
+
# We wrap the non-masked arithmetic logic used for numpy dtypes
|
|
921
|
+
# in Series/Index arithmetic ops.
|
|
922
|
+
other = ops.maybe_prepare_scalar_for_op(other, (len(self),))
|
|
923
|
+
pd_op = ops.get_array_op(op)
|
|
924
|
+
other = ensure_wrapped_if_datetimelike(other)
|
|
925
|
+
|
|
926
|
+
if isinstance(other, ExtensionArray) and isinstance(other.dtype, ArrowDtype):
|
|
927
|
+
# GH#58602
|
|
928
|
+
return NotImplemented
|
|
929
|
+
|
|
930
|
+
if op_name in {"pow", "rpow"} and isinstance(other, np.bool_):
|
|
931
|
+
# Avoid DeprecationWarning: In future, it will be an error
|
|
932
|
+
# for 'np.bool_' scalars to be interpreted as an index
|
|
933
|
+
# e.g. test_array_scalar_like_equivalence
|
|
934
|
+
other = bool(other)
|
|
935
|
+
|
|
936
|
+
mask = self._propagate_mask(omask, other)
|
|
937
|
+
|
|
938
|
+
if other is libmissing.NA:
|
|
939
|
+
result = np.ones_like(self._data)
|
|
940
|
+
if self.dtype.kind == "b":
|
|
941
|
+
if op_name in {
|
|
942
|
+
"floordiv",
|
|
943
|
+
"rfloordiv",
|
|
944
|
+
"pow",
|
|
945
|
+
"rpow",
|
|
946
|
+
"truediv",
|
|
947
|
+
"rtruediv",
|
|
948
|
+
}:
|
|
949
|
+
# GH#41165 Try to match non-masked Series behavior
|
|
950
|
+
# This is still imperfect GH#46043
|
|
951
|
+
raise NotImplementedError(
|
|
952
|
+
f"operator '{op_name}' not implemented for bool dtypes"
|
|
953
|
+
)
|
|
954
|
+
if op_name in {"mod", "rmod"}:
|
|
955
|
+
dtype = "int8"
|
|
956
|
+
else:
|
|
957
|
+
dtype = "bool"
|
|
958
|
+
result = result.astype(dtype)
|
|
959
|
+
elif "truediv" in op_name and self.dtype.kind != "f":
|
|
960
|
+
# The actual data here doesn't matter since the mask
|
|
961
|
+
# will be all-True, but since this is division, we want
|
|
962
|
+
# to end up with floating dtype.
|
|
963
|
+
result = result.astype(np.float64)
|
|
964
|
+
elif op_name in {"divmod", "rdivmod"}:
|
|
965
|
+
# GH#62196
|
|
966
|
+
res = self._maybe_mask_result(result, mask)
|
|
967
|
+
return res, res.copy()
|
|
968
|
+
else:
|
|
969
|
+
# Make sure we do this before the "pow" mask checks
|
|
970
|
+
# to get an expected exception message on shape mismatch.
|
|
971
|
+
if self.dtype.kind in "iu" and op_name in ["floordiv", "mod"]:
|
|
972
|
+
# TODO(GH#30188) ATM we don't match the behavior of non-masked
|
|
973
|
+
# types with respect to floordiv-by-zero
|
|
974
|
+
pd_op = op
|
|
975
|
+
|
|
976
|
+
with np.errstate(all="ignore"):
|
|
977
|
+
result = pd_op(self._data, other)
|
|
978
|
+
|
|
979
|
+
if op_name == "pow":
|
|
980
|
+
# 1 ** x is 1.
|
|
981
|
+
mask = np.where((self._data == 1) & ~self._mask, False, mask)
|
|
982
|
+
# x ** 0 is 1.
|
|
983
|
+
if omask is not None:
|
|
984
|
+
mask = np.where((other == 0) & ~omask, False, mask)
|
|
985
|
+
elif other is not libmissing.NA:
|
|
986
|
+
mask = np.where(other == 0, False, mask)
|
|
987
|
+
|
|
988
|
+
elif op_name == "rpow":
|
|
989
|
+
# 1 ** x is 1.
|
|
990
|
+
if omask is not None:
|
|
991
|
+
mask = np.where((other == 1) & ~omask, False, mask)
|
|
992
|
+
elif other is not libmissing.NA:
|
|
993
|
+
mask = np.where(other == 1, False, mask)
|
|
994
|
+
# x ** 0 is 1.
|
|
995
|
+
mask = np.where((self._data == 0) & ~self._mask, False, mask)
|
|
996
|
+
|
|
997
|
+
return self._maybe_mask_result(result, mask)
|
|
998
|
+
|
|
999
|
+
_logical_method = _arith_method
|
|
1000
|
+
|
|
1001
|
+
def _cmp_method(self, other, op) -> BooleanArray:
|
|
1002
|
+
from pandas.core.arrays import BooleanArray
|
|
1003
|
+
|
|
1004
|
+
mask = None
|
|
1005
|
+
|
|
1006
|
+
if isinstance(other, ExtensionArray) and isinstance(other.dtype, ArrowDtype):
|
|
1007
|
+
# GH#58602
|
|
1008
|
+
return NotImplemented
|
|
1009
|
+
|
|
1010
|
+
elif isinstance(other, BaseMaskedArray):
|
|
1011
|
+
other, mask = other._data, other._mask
|
|
1012
|
+
|
|
1013
|
+
elif is_list_like(other):
|
|
1014
|
+
if not isinstance(
|
|
1015
|
+
other, (list, np.ndarray, ExtensionArray)
|
|
1016
|
+
) and not ops.has_castable_attr(other):
|
|
1017
|
+
warnings.warn(
|
|
1018
|
+
f"Operation with {type(other).__name__} is deprecated. "
|
|
1019
|
+
"In a future version these will be treated as scalar-like. "
|
|
1020
|
+
"To retain the old behavior, explicitly wrap in a Series "
|
|
1021
|
+
"instead.",
|
|
1022
|
+
Pandas4Warning,
|
|
1023
|
+
stacklevel=find_stack_level(),
|
|
1024
|
+
)
|
|
1025
|
+
other = np.asarray(other)
|
|
1026
|
+
if other.ndim > 1:
|
|
1027
|
+
raise NotImplementedError("can only perform ops with 1-d structures")
|
|
1028
|
+
if len(self) != len(other):
|
|
1029
|
+
raise ValueError("Lengths must match to compare")
|
|
1030
|
+
|
|
1031
|
+
if other is libmissing.NA:
|
|
1032
|
+
# numpy does not handle pd.NA well as "other" scalar (it returns
|
|
1033
|
+
# a scalar False instead of an array)
|
|
1034
|
+
# This may be fixed by NA.__array_ufunc__. Revisit this check
|
|
1035
|
+
# once that's implemented.
|
|
1036
|
+
result = np.zeros(self._data.shape, dtype="bool")
|
|
1037
|
+
mask = np.ones(self._data.shape, dtype="bool")
|
|
1038
|
+
else:
|
|
1039
|
+
with warnings.catch_warnings():
|
|
1040
|
+
# numpy may show a FutureWarning or DeprecationWarning:
|
|
1041
|
+
# elementwise comparison failed; returning scalar instead,
|
|
1042
|
+
# but in the future will perform elementwise comparison
|
|
1043
|
+
# before returning NotImplemented. We fall back to the correct
|
|
1044
|
+
# behavior today, so that should be fine to ignore.
|
|
1045
|
+
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
|
|
1046
|
+
warnings.filterwarnings("ignore", "elementwise", DeprecationWarning)
|
|
1047
|
+
method = getattr(self._data, f"__{op.__name__}__")
|
|
1048
|
+
result = method(other)
|
|
1049
|
+
|
|
1050
|
+
if result is NotImplemented:
|
|
1051
|
+
result = invalid_comparison(self._data, other, op)
|
|
1052
|
+
|
|
1053
|
+
mask = self._propagate_mask(mask, other)
|
|
1054
|
+
return BooleanArray(result, mask, copy=False)
|
|
1055
|
+
|
|
1056
|
+
def _maybe_mask_result(
|
|
1057
|
+
self, result: np.ndarray | tuple[np.ndarray, np.ndarray], mask: np.ndarray
|
|
1058
|
+
):
|
|
1059
|
+
"""
|
|
1060
|
+
Parameters
|
|
1061
|
+
----------
|
|
1062
|
+
result : array-like or tuple[array-like]
|
|
1063
|
+
mask : array-like bool
|
|
1064
|
+
"""
|
|
1065
|
+
if isinstance(result, tuple):
|
|
1066
|
+
# i.e. divmod
|
|
1067
|
+
div, mod = result
|
|
1068
|
+
return (
|
|
1069
|
+
self._maybe_mask_result(div, mask),
|
|
1070
|
+
self._maybe_mask_result(mod, mask),
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
if result.dtype.kind == "f":
|
|
1074
|
+
from pandas.core.arrays import FloatingArray
|
|
1075
|
+
|
|
1076
|
+
if is_nan_na():
|
|
1077
|
+
mask[np.isnan(result)] = True
|
|
1078
|
+
|
|
1079
|
+
return FloatingArray(result, mask, copy=False)
|
|
1080
|
+
|
|
1081
|
+
elif result.dtype.kind == "b":
|
|
1082
|
+
from pandas.core.arrays import BooleanArray
|
|
1083
|
+
|
|
1084
|
+
return BooleanArray(result, mask, copy=False)
|
|
1085
|
+
|
|
1086
|
+
elif lib.is_np_dtype(result.dtype, "m") and is_supported_dtype(result.dtype):
|
|
1087
|
+
# e.g. test_numeric_arr_mul_tdscalar_numexpr_path
|
|
1088
|
+
from pandas.core.arrays import TimedeltaArray
|
|
1089
|
+
|
|
1090
|
+
unit = np.datetime_data(result.dtype)[0]
|
|
1091
|
+
result[mask] = np.timedelta64("NaT", unit) # type: ignore[call-overload]
|
|
1092
|
+
|
|
1093
|
+
if not isinstance(result, TimedeltaArray):
|
|
1094
|
+
return TimedeltaArray._simple_new(result, dtype=result.dtype)
|
|
1095
|
+
|
|
1096
|
+
return result
|
|
1097
|
+
|
|
1098
|
+
elif result.dtype.kind in "iu":
|
|
1099
|
+
from pandas.core.arrays import IntegerArray
|
|
1100
|
+
|
|
1101
|
+
return IntegerArray(result, mask, copy=False)
|
|
1102
|
+
|
|
1103
|
+
elif result.dtype == object:
|
|
1104
|
+
result[mask] = self.dtype.na_value
|
|
1105
|
+
return result
|
|
1106
|
+
else:
|
|
1107
|
+
result[mask] = np.nan
|
|
1108
|
+
return result
|
|
1109
|
+
|
|
1110
|
+
def isna(self) -> np.ndarray:
|
|
1111
|
+
return self._mask.copy()
|
|
1112
|
+
|
|
1113
|
+
@property
|
|
1114
|
+
def _na_value(self):
|
|
1115
|
+
return self.dtype.na_value
|
|
1116
|
+
|
|
1117
|
+
@property
|
|
1118
|
+
def nbytes(self) -> int:
|
|
1119
|
+
return self._data.nbytes + self._mask.nbytes
|
|
1120
|
+
|
|
1121
|
+
@classmethod
|
|
1122
|
+
def _concat_same_type(
|
|
1123
|
+
cls,
|
|
1124
|
+
to_concat: Sequence[Self],
|
|
1125
|
+
axis: AxisInt = 0,
|
|
1126
|
+
) -> Self:
|
|
1127
|
+
data = np.concatenate([x._data for x in to_concat], axis=axis)
|
|
1128
|
+
mask = np.concatenate([x._mask for x in to_concat], axis=axis)
|
|
1129
|
+
return cls(data, mask)
|
|
1130
|
+
|
|
1131
|
+
def _hash_pandas_object(
|
|
1132
|
+
self, *, encoding: str, hash_key: str, categorize: bool
|
|
1133
|
+
) -> npt.NDArray[np.uint64]:
|
|
1134
|
+
hashed_array = hash_array(
|
|
1135
|
+
self._data, encoding=encoding, hash_key=hash_key, categorize=categorize
|
|
1136
|
+
)
|
|
1137
|
+
hashed_array[self.isna()] = hash(self.dtype.na_value)
|
|
1138
|
+
return hashed_array
|
|
1139
|
+
|
|
1140
|
+
def take(
|
|
1141
|
+
self,
|
|
1142
|
+
indexer,
|
|
1143
|
+
*,
|
|
1144
|
+
allow_fill: bool = False,
|
|
1145
|
+
fill_value: Scalar | None = None,
|
|
1146
|
+
axis: AxisInt = 0,
|
|
1147
|
+
) -> Self:
|
|
1148
|
+
# we always fill with 1 internally
|
|
1149
|
+
# to avoid upcasting
|
|
1150
|
+
data_fill_value = (
|
|
1151
|
+
self.dtype._internal_fill_value if isna(fill_value) else fill_value
|
|
1152
|
+
)
|
|
1153
|
+
result = take(
|
|
1154
|
+
self._data,
|
|
1155
|
+
indexer,
|
|
1156
|
+
fill_value=data_fill_value,
|
|
1157
|
+
allow_fill=allow_fill,
|
|
1158
|
+
axis=axis,
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
mask = take(
|
|
1162
|
+
self._mask, indexer, fill_value=True, allow_fill=allow_fill, axis=axis
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1165
|
+
# if we are filling
|
|
1166
|
+
# we only fill where the indexer is null
|
|
1167
|
+
# not existing missing values
|
|
1168
|
+
# TODO(jreback) what if we have a non-na float as a fill value?
|
|
1169
|
+
if allow_fill and notna(fill_value):
|
|
1170
|
+
fill_mask = np.asarray(indexer) == -1
|
|
1171
|
+
result[fill_mask] = fill_value
|
|
1172
|
+
mask = mask ^ fill_mask
|
|
1173
|
+
|
|
1174
|
+
return self._simple_new(result, mask)
|
|
1175
|
+
|
|
1176
|
+
# error: Return type "BooleanArray" of "isin" incompatible with return type
|
|
1177
|
+
# "ndarray" in supertype "ExtensionArray"
|
|
1178
|
+
def isin(self, values: ArrayLike) -> BooleanArray: # type: ignore[override]
|
|
1179
|
+
from pandas.core.arrays import BooleanArray
|
|
1180
|
+
|
|
1181
|
+
# algorithms.isin will eventually convert values to an ndarray, so no extra
|
|
1182
|
+
# cost to doing it here first
|
|
1183
|
+
values_arr = np.asarray(values)
|
|
1184
|
+
result = isin(self._data, values_arr)
|
|
1185
|
+
|
|
1186
|
+
if self._hasna:
|
|
1187
|
+
values_have_NA = values_arr.dtype == object and any(
|
|
1188
|
+
val is self.dtype.na_value for val in values_arr
|
|
1189
|
+
)
|
|
1190
|
+
|
|
1191
|
+
# For now, NA does not propagate so set result according to presence of NA,
|
|
1192
|
+
# see https://github.com/pandas-dev/pandas/pull/38379 for some discussion
|
|
1193
|
+
result[self._mask] = values_have_NA
|
|
1194
|
+
|
|
1195
|
+
mask = np.zeros(self._data.shape, dtype=bool)
|
|
1196
|
+
return BooleanArray(result, mask, copy=False)
|
|
1197
|
+
|
|
1198
|
+
def copy(self) -> Self:
|
|
1199
|
+
data = self._data.copy()
|
|
1200
|
+
mask = self._mask.copy()
|
|
1201
|
+
return self._simple_new(data, mask)
|
|
1202
|
+
|
|
1203
|
+
@overload
|
|
1204
|
+
def view(self, dtype: None = ...) -> Self: ...
|
|
1205
|
+
|
|
1206
|
+
@overload
|
|
1207
|
+
def view(self, dtype: Dtype | None = ...) -> ArrayLike: ...
|
|
1208
|
+
|
|
1209
|
+
def view(self, dtype: Dtype | None = None) -> ArrayLike:
|
|
1210
|
+
if dtype is not None:
|
|
1211
|
+
return super().view(dtype)
|
|
1212
|
+
result = self._simple_new(self._data[:], self._mask[:])
|
|
1213
|
+
result._readonly = self._readonly
|
|
1214
|
+
return result
|
|
1215
|
+
|
|
1216
|
+
@property
|
|
1217
|
+
def _is_monotonic_increasing(self) -> bool:
|
|
1218
|
+
if self._hasna:
|
|
1219
|
+
return False
|
|
1220
|
+
data = self._data
|
|
1221
|
+
if data.dtype.kind == "b":
|
|
1222
|
+
data = data.view("uint8")
|
|
1223
|
+
return libalgos.is_monotonic(data, timelike=False)[0]
|
|
1224
|
+
|
|
1225
|
+
@property
|
|
1226
|
+
def _is_monotonic_decreasing(self) -> bool:
|
|
1227
|
+
if self._hasna:
|
|
1228
|
+
return False
|
|
1229
|
+
data = self._data
|
|
1230
|
+
if data.dtype.kind == "b":
|
|
1231
|
+
data = data.view("uint8")
|
|
1232
|
+
return libalgos.is_monotonic(data, timelike=False)[1]
|
|
1233
|
+
|
|
1234
|
+
def _rank(
|
|
1235
|
+
self,
|
|
1236
|
+
*,
|
|
1237
|
+
axis: AxisInt = 0,
|
|
1238
|
+
method: str = "average",
|
|
1239
|
+
na_option: str = "keep",
|
|
1240
|
+
ascending: bool = True,
|
|
1241
|
+
pct: bool = False,
|
|
1242
|
+
):
|
|
1243
|
+
# GH#62043 Avoid going through copy-making ensure_data in algorithms.rank
|
|
1244
|
+
if axis != 0 or self.ndim != 1:
|
|
1245
|
+
raise NotImplementedError
|
|
1246
|
+
|
|
1247
|
+
from pandas.core.arrays import FloatingArray
|
|
1248
|
+
|
|
1249
|
+
data = self._data
|
|
1250
|
+
if data.dtype.kind == "b":
|
|
1251
|
+
data = data.view("uint8")
|
|
1252
|
+
|
|
1253
|
+
result = libalgos.rank_1d(
|
|
1254
|
+
data,
|
|
1255
|
+
is_datetimelike=False,
|
|
1256
|
+
ties_method=method,
|
|
1257
|
+
ascending=ascending,
|
|
1258
|
+
na_option=na_option,
|
|
1259
|
+
pct=pct,
|
|
1260
|
+
mask=self.isna(),
|
|
1261
|
+
)
|
|
1262
|
+
if na_option in ["top", "bottom"]:
|
|
1263
|
+
mask = np.zeros(self.shape, dtype=bool)
|
|
1264
|
+
else:
|
|
1265
|
+
mask = self._mask.copy()
|
|
1266
|
+
|
|
1267
|
+
if method != "average" and not pct:
|
|
1268
|
+
if na_option not in ["top", "bottom"]:
|
|
1269
|
+
result[self._mask] = 0 # avoid warning on casting
|
|
1270
|
+
result = result.astype("uint64", copy=False)
|
|
1271
|
+
from pandas.core.arrays import IntegerArray
|
|
1272
|
+
|
|
1273
|
+
return IntegerArray(result, mask=mask)
|
|
1274
|
+
|
|
1275
|
+
return FloatingArray(result, mask=mask)
|
|
1276
|
+
|
|
1277
|
+
def duplicated(
|
|
1278
|
+
self, keep: Literal["first", "last", False] = "first"
|
|
1279
|
+
) -> npt.NDArray[np.bool_]:
|
|
1280
|
+
"""
|
|
1281
|
+
Return boolean ndarray denoting duplicate values.
|
|
1282
|
+
|
|
1283
|
+
Parameters
|
|
1284
|
+
----------
|
|
1285
|
+
keep : {'first', 'last', False}, default 'first'
|
|
1286
|
+
- ``first`` : Mark duplicates as ``True`` except for the first occurrence.
|
|
1287
|
+
- ``last`` : Mark duplicates as ``True`` except for the last occurrence.
|
|
1288
|
+
- False : Mark all duplicates as ``True``.
|
|
1289
|
+
|
|
1290
|
+
Returns
|
|
1291
|
+
-------
|
|
1292
|
+
ndarray[bool]
|
|
1293
|
+
With true in indices where elements are duplicated and false otherwise.
|
|
1294
|
+
|
|
1295
|
+
See Also
|
|
1296
|
+
--------
|
|
1297
|
+
DataFrame.duplicated : Return boolean Series denoting
|
|
1298
|
+
duplicate rows.
|
|
1299
|
+
Series.duplicated : Indicate duplicate Series values.
|
|
1300
|
+
api.extensions.ExtensionArray.unique : Compute the ExtensionArray
|
|
1301
|
+
of unique values.
|
|
1302
|
+
|
|
1303
|
+
Examples
|
|
1304
|
+
--------
|
|
1305
|
+
>>> pd.array([1, 1, 2, 3, 3], dtype="Int64").duplicated()
|
|
1306
|
+
array([False, True, False, False, True])
|
|
1307
|
+
"""
|
|
1308
|
+
values = self._data
|
|
1309
|
+
mask = self._mask
|
|
1310
|
+
return algos.duplicated(values, keep=keep, mask=mask)
|
|
1311
|
+
|
|
1312
|
+
def unique(self) -> Self:
|
|
1313
|
+
"""
|
|
1314
|
+
Compute the BaseMaskedArray of unique values.
|
|
1315
|
+
|
|
1316
|
+
Returns
|
|
1317
|
+
-------
|
|
1318
|
+
uniques : BaseMaskedArray
|
|
1319
|
+
"""
|
|
1320
|
+
uniques, mask = algos.unique_with_mask(self._data, self._mask)
|
|
1321
|
+
return self._simple_new(uniques, mask)
|
|
1322
|
+
|
|
1323
|
+
def searchsorted(
|
|
1324
|
+
self,
|
|
1325
|
+
value: NumpyValueArrayLike | ExtensionArray,
|
|
1326
|
+
side: Literal["left", "right"] = "left",
|
|
1327
|
+
sorter: NumpySorter | None = None,
|
|
1328
|
+
) -> npt.NDArray[np.intp] | np.intp:
|
|
1329
|
+
"""
|
|
1330
|
+
Find indices where elements should be inserted to maintain order.
|
|
1331
|
+
|
|
1332
|
+
Find the indices into a sorted array `self` (a) such that, if the
|
|
1333
|
+
corresponding elements in `value` were inserted before the indices,
|
|
1334
|
+
the order of `self` would be preserved.
|
|
1335
|
+
|
|
1336
|
+
Assuming that `self` is sorted:
|
|
1337
|
+
|
|
1338
|
+
====== ================================
|
|
1339
|
+
`side` returned index `i` satisfies
|
|
1340
|
+
====== ================================
|
|
1341
|
+
left ``self[i-1] < value <= self[i]``
|
|
1342
|
+
right ``self[i-1] <= value < self[i]``
|
|
1343
|
+
====== ================================
|
|
1344
|
+
|
|
1345
|
+
Parameters
|
|
1346
|
+
----------
|
|
1347
|
+
value : array-like, list or scalar
|
|
1348
|
+
Value(s) to insert into `self`.
|
|
1349
|
+
side : {'left', 'right'}, optional
|
|
1350
|
+
If 'left', the index of the first suitable location found is given.
|
|
1351
|
+
If 'right', return the last such index. If there is no suitable
|
|
1352
|
+
index, return either 0 or N (where N is the length of `self`).
|
|
1353
|
+
sorter : 1-D array-like, optional
|
|
1354
|
+
Optional array of integer indices that sort array a into ascending
|
|
1355
|
+
order. They are typically the result of argsort.
|
|
1356
|
+
|
|
1357
|
+
Returns
|
|
1358
|
+
-------
|
|
1359
|
+
array of ints or int
|
|
1360
|
+
If value is array-like, array of insertion points.
|
|
1361
|
+
If value is scalar, a single integer.
|
|
1362
|
+
|
|
1363
|
+
See Also
|
|
1364
|
+
--------
|
|
1365
|
+
numpy.searchsorted : Similar method from NumPy.
|
|
1366
|
+
|
|
1367
|
+
Examples
|
|
1368
|
+
--------
|
|
1369
|
+
>>> arr = pd.array([1, 2, 3, 5])
|
|
1370
|
+
>>> arr.searchsorted([4])
|
|
1371
|
+
array([3])
|
|
1372
|
+
"""
|
|
1373
|
+
if self._hasna:
|
|
1374
|
+
raise ValueError(
|
|
1375
|
+
"searchsorted requires array to be sorted, which is impossible "
|
|
1376
|
+
"with NAs present."
|
|
1377
|
+
)
|
|
1378
|
+
if isinstance(value, ExtensionArray):
|
|
1379
|
+
value = value.astype(object)
|
|
1380
|
+
# Base class searchsorted would cast to object, which is *much* slower.
|
|
1381
|
+
return self._data.searchsorted(value, side=side, sorter=sorter)
|
|
1382
|
+
|
|
1383
|
+
def factorize(
|
|
1384
|
+
self,
|
|
1385
|
+
use_na_sentinel: bool = True,
|
|
1386
|
+
) -> tuple[np.ndarray, ExtensionArray]:
|
|
1387
|
+
"""
|
|
1388
|
+
Encode the extension array as an enumerated type.
|
|
1389
|
+
|
|
1390
|
+
Parameters
|
|
1391
|
+
----------
|
|
1392
|
+
use_na_sentinel : bool, default True
|
|
1393
|
+
If True, the sentinel -1 will be used for NaN values. If False,
|
|
1394
|
+
NaN values will be encoded as non-negative integers and will not drop the
|
|
1395
|
+
NaN from the uniques of the values.
|
|
1396
|
+
|
|
1397
|
+
Returns
|
|
1398
|
+
-------
|
|
1399
|
+
codes : ndarray
|
|
1400
|
+
An integer NumPy array that's an indexer into the original
|
|
1401
|
+
ExtensionArray.
|
|
1402
|
+
uniques : ExtensionArray
|
|
1403
|
+
An ExtensionArray containing the unique values of `self`.
|
|
1404
|
+
|
|
1405
|
+
.. note::
|
|
1406
|
+
|
|
1407
|
+
uniques will *not* contain an entry for the NA value of
|
|
1408
|
+
the ExtensionArray if there are any missing values present
|
|
1409
|
+
in `self`.
|
|
1410
|
+
|
|
1411
|
+
See Also
|
|
1412
|
+
--------
|
|
1413
|
+
factorize : Top-level factorize method that dispatches here.
|
|
1414
|
+
|
|
1415
|
+
Notes
|
|
1416
|
+
-----
|
|
1417
|
+
:meth:`pandas.factorize` offers a `sort` keyword as well.
|
|
1418
|
+
|
|
1419
|
+
Examples
|
|
1420
|
+
--------
|
|
1421
|
+
>>> idx1 = pd.PeriodIndex(
|
|
1422
|
+
... ["2014-01", "2014-01", "2014-02", "2014-02", "2014-03", "2014-03"],
|
|
1423
|
+
... freq="M",
|
|
1424
|
+
... )
|
|
1425
|
+
>>> arr, idx = idx1.factorize()
|
|
1426
|
+
>>> arr
|
|
1427
|
+
array([0, 0, 1, 1, 2, 2])
|
|
1428
|
+
>>> idx
|
|
1429
|
+
PeriodIndex(['2014-01', '2014-02', '2014-03'], dtype='period[M]')
|
|
1430
|
+
"""
|
|
1431
|
+
arr = self._data
|
|
1432
|
+
mask = self._mask
|
|
1433
|
+
|
|
1434
|
+
# Use a sentinel for na; recode and add NA to uniques if necessary below
|
|
1435
|
+
codes, uniques = factorize_array(arr, use_na_sentinel=True, mask=mask)
|
|
1436
|
+
|
|
1437
|
+
# check that factorize_array correctly preserves dtype.
|
|
1438
|
+
assert uniques.dtype == self.dtype.numpy_dtype, (uniques.dtype, self.dtype)
|
|
1439
|
+
|
|
1440
|
+
has_na = mask.any()
|
|
1441
|
+
if use_na_sentinel or not has_na:
|
|
1442
|
+
size = len(uniques)
|
|
1443
|
+
else:
|
|
1444
|
+
# Make room for an NA value
|
|
1445
|
+
size = len(uniques) + 1
|
|
1446
|
+
uniques_mask = np.zeros(size, dtype=bool)
|
|
1447
|
+
if not use_na_sentinel and has_na:
|
|
1448
|
+
na_index = mask.argmax()
|
|
1449
|
+
# Insert na with the proper code
|
|
1450
|
+
if na_index == 0:
|
|
1451
|
+
na_code = np.intp(0)
|
|
1452
|
+
else:
|
|
1453
|
+
na_code = codes[:na_index].max() + 1
|
|
1454
|
+
codes[codes >= na_code] += 1
|
|
1455
|
+
codes[codes == -1] = na_code
|
|
1456
|
+
# dummy value for uniques; not used since uniques_mask will be True
|
|
1457
|
+
uniques = np.insert(uniques, na_code, 0)
|
|
1458
|
+
uniques_mask[na_code] = True
|
|
1459
|
+
uniques_ea = self._simple_new(uniques, uniques_mask)
|
|
1460
|
+
|
|
1461
|
+
return codes, uniques_ea
|
|
1462
|
+
|
|
1463
|
+
def _values_for_argsort(self) -> np.ndarray:
|
|
1464
|
+
"""
|
|
1465
|
+
Return values for sorting.
|
|
1466
|
+
|
|
1467
|
+
Returns
|
|
1468
|
+
-------
|
|
1469
|
+
ndarray
|
|
1470
|
+
The transformed values should maintain the ordering between values
|
|
1471
|
+
within the array.
|
|
1472
|
+
|
|
1473
|
+
See Also
|
|
1474
|
+
--------
|
|
1475
|
+
ExtensionArray.argsort : Return the indices that would sort this array.
|
|
1476
|
+
|
|
1477
|
+
Notes
|
|
1478
|
+
-----
|
|
1479
|
+
The caller is responsible for *not* modifying these values in-place, so
|
|
1480
|
+
it is safe for implementers to give views on ``self``.
|
|
1481
|
+
|
|
1482
|
+
Functions that use this (e.g. ``ExtensionArray.argsort``) should ignore
|
|
1483
|
+
entries with missing values in the original array (according to
|
|
1484
|
+
``self.isna()``). This means that the corresponding entries in the returned
|
|
1485
|
+
array don't need to be modified to sort correctly.
|
|
1486
|
+
|
|
1487
|
+
Examples
|
|
1488
|
+
--------
|
|
1489
|
+
In most cases, this is the underlying Numpy array of the ``ExtensionArray``:
|
|
1490
|
+
|
|
1491
|
+
>>> arr = pd.array([1, 2, 3])
|
|
1492
|
+
>>> arr._values_for_argsort()
|
|
1493
|
+
array([1, 2, 3])
|
|
1494
|
+
"""
|
|
1495
|
+
return self._data
|
|
1496
|
+
|
|
1497
|
+
def value_counts(self, dropna: bool = True) -> Series:
|
|
1498
|
+
"""
|
|
1499
|
+
Returns a Series containing counts of each unique value.
|
|
1500
|
+
|
|
1501
|
+
Parameters
|
|
1502
|
+
----------
|
|
1503
|
+
dropna : bool, default True
|
|
1504
|
+
Don't include counts of missing values.
|
|
1505
|
+
|
|
1506
|
+
Returns
|
|
1507
|
+
-------
|
|
1508
|
+
counts : Series
|
|
1509
|
+
|
|
1510
|
+
See Also
|
|
1511
|
+
--------
|
|
1512
|
+
Series.value_counts
|
|
1513
|
+
"""
|
|
1514
|
+
from pandas import (
|
|
1515
|
+
Index,
|
|
1516
|
+
Series,
|
|
1517
|
+
)
|
|
1518
|
+
from pandas.arrays import IntegerArray
|
|
1519
|
+
|
|
1520
|
+
keys, value_counts, na_counter = algos.value_counts_arraylike(
|
|
1521
|
+
self._data, dropna=dropna, mask=self._mask
|
|
1522
|
+
)
|
|
1523
|
+
mask_index = np.zeros((len(value_counts),), dtype=np.bool_)
|
|
1524
|
+
mask = mask_index.copy()
|
|
1525
|
+
|
|
1526
|
+
if na_counter > 0:
|
|
1527
|
+
mask_index[-1] = True
|
|
1528
|
+
|
|
1529
|
+
arr = IntegerArray(value_counts, mask)
|
|
1530
|
+
index = Index(
|
|
1531
|
+
self.dtype.construct_array_type()(
|
|
1532
|
+
keys, # type: ignore[arg-type]
|
|
1533
|
+
mask_index,
|
|
1534
|
+
),
|
|
1535
|
+
copy=False,
|
|
1536
|
+
)
|
|
1537
|
+
return Series(arr, index=index, name="count", copy=False)
|
|
1538
|
+
|
|
1539
|
+
def _mode(self, dropna: bool = True) -> Self:
|
|
1540
|
+
result, res_mask = mode(self._data, dropna=dropna, mask=self._mask)
|
|
1541
|
+
result = type(self)(result, res_mask)
|
|
1542
|
+
return result[result.argsort()]
|
|
1543
|
+
|
|
1544
|
+
def equals(self, other) -> bool:
|
|
1545
|
+
"""
|
|
1546
|
+
Return if another array is equivalent to this array.
|
|
1547
|
+
|
|
1548
|
+
Equivalent means that both arrays have the same shape and dtype, and
|
|
1549
|
+
all values compare equal. Missing values in the same location are
|
|
1550
|
+
considered equal (in contrast with normal equality).
|
|
1551
|
+
|
|
1552
|
+
Parameters
|
|
1553
|
+
----------
|
|
1554
|
+
other : ExtensionArray
|
|
1555
|
+
Array to compare to this Array.
|
|
1556
|
+
|
|
1557
|
+
Returns
|
|
1558
|
+
-------
|
|
1559
|
+
boolean
|
|
1560
|
+
Whether the arrays are equivalent.
|
|
1561
|
+
|
|
1562
|
+
See Also
|
|
1563
|
+
--------
|
|
1564
|
+
numpy.array_equal : Equivalent method for numpy array.
|
|
1565
|
+
Series.equals : Equivalent method for Series.
|
|
1566
|
+
DataFrame.equals : Equivalent method for DataFrame.
|
|
1567
|
+
|
|
1568
|
+
Examples
|
|
1569
|
+
--------
|
|
1570
|
+
>>> arr1 = pd.array([1, 2, np.nan])
|
|
1571
|
+
>>> arr2 = pd.array([1, 2, np.nan])
|
|
1572
|
+
>>> arr1.equals(arr2)
|
|
1573
|
+
True
|
|
1574
|
+
|
|
1575
|
+
>>> arr1 = pd.array([1, 3, np.nan])
|
|
1576
|
+
>>> arr2 = pd.array([1, 2, np.nan])
|
|
1577
|
+
>>> arr1.equals(arr2)
|
|
1578
|
+
False
|
|
1579
|
+
"""
|
|
1580
|
+
if type(self) != type(other):
|
|
1581
|
+
return False
|
|
1582
|
+
if other.dtype != self.dtype:
|
|
1583
|
+
return False
|
|
1584
|
+
|
|
1585
|
+
# GH#44382 if e.g. self[1] is np.nan and other[1] is pd.NA, we are NOT
|
|
1586
|
+
# equal.
|
|
1587
|
+
if not np.array_equal(self._mask, other._mask):
|
|
1588
|
+
return False
|
|
1589
|
+
|
|
1590
|
+
left = self._data[~self._mask]
|
|
1591
|
+
right = other._data[~other._mask]
|
|
1592
|
+
return array_equivalent(left, right, strict_nan=True, dtype_equal=True)
|
|
1593
|
+
|
|
1594
|
+
def _quantile(
|
|
1595
|
+
self, qs: npt.NDArray[np.float64], interpolation: str
|
|
1596
|
+
) -> BaseMaskedArray:
|
|
1597
|
+
"""
|
|
1598
|
+
Dispatch to quantile_with_mask, needed because we do not have
|
|
1599
|
+
_from_factorized.
|
|
1600
|
+
|
|
1601
|
+
Notes
|
|
1602
|
+
-----
|
|
1603
|
+
We assume that all impacted cases are 1D-only.
|
|
1604
|
+
"""
|
|
1605
|
+
res = quantile_with_mask(
|
|
1606
|
+
self._data,
|
|
1607
|
+
mask=self._mask,
|
|
1608
|
+
# TODO(GH#40932): na_value_for_dtype(self.dtype.numpy_dtype)
|
|
1609
|
+
# instead of np.nan
|
|
1610
|
+
fill_value=np.nan,
|
|
1611
|
+
qs=qs,
|
|
1612
|
+
interpolation=interpolation,
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
if self._hasna:
|
|
1616
|
+
# Our result mask is all-False unless we are all-NA, in which
|
|
1617
|
+
# case it is all-True.
|
|
1618
|
+
if self.ndim == 2:
|
|
1619
|
+
# I think this should be out_mask=self.isna().all(axis=1)
|
|
1620
|
+
# but am holding off until we have tests
|
|
1621
|
+
raise NotImplementedError
|
|
1622
|
+
if self.isna().all():
|
|
1623
|
+
out_mask = np.ones(res.shape, dtype=bool)
|
|
1624
|
+
|
|
1625
|
+
if is_integer_dtype(self.dtype):
|
|
1626
|
+
# We try to maintain int dtype if possible for not all-na case
|
|
1627
|
+
# as well
|
|
1628
|
+
res = np.zeros(res.shape, dtype=self.dtype.numpy_dtype)
|
|
1629
|
+
else:
|
|
1630
|
+
out_mask = np.zeros(res.shape, dtype=bool)
|
|
1631
|
+
else:
|
|
1632
|
+
out_mask = np.zeros(res.shape, dtype=bool)
|
|
1633
|
+
return self._maybe_mask_result(res, mask=out_mask)
|
|
1634
|
+
|
|
1635
|
+
# ------------------------------------------------------------------
|
|
1636
|
+
# Reductions
|
|
1637
|
+
|
|
1638
|
+
def _reduce(
|
|
1639
|
+
self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs
|
|
1640
|
+
):
|
|
1641
|
+
if name in {"any", "all", "min", "max", "sum", "prod", "mean", "var", "std"}:
|
|
1642
|
+
result = getattr(self, name)(skipna=skipna, **kwargs)
|
|
1643
|
+
else:
|
|
1644
|
+
# median, skew, kurt, sem
|
|
1645
|
+
data = self._data
|
|
1646
|
+
mask = self._mask
|
|
1647
|
+
op = getattr(nanops, f"nan{name}")
|
|
1648
|
+
axis = kwargs.pop("axis", None)
|
|
1649
|
+
result = op(data, axis=axis, skipna=skipna, mask=mask, **kwargs)
|
|
1650
|
+
|
|
1651
|
+
if keepdims:
|
|
1652
|
+
if isna(result):
|
|
1653
|
+
return self._wrap_na_result(name=name, axis=0, mask_size=(1,))
|
|
1654
|
+
else:
|
|
1655
|
+
if using_python_scalars():
|
|
1656
|
+
result = np.array([result])
|
|
1657
|
+
else:
|
|
1658
|
+
result = result.reshape(1)
|
|
1659
|
+
mask = np.zeros(1, dtype=bool)
|
|
1660
|
+
return self._maybe_mask_result(result, mask)
|
|
1661
|
+
|
|
1662
|
+
if isna(result):
|
|
1663
|
+
return libmissing.NA
|
|
1664
|
+
else:
|
|
1665
|
+
return result
|
|
1666
|
+
|
|
1667
|
+
def _wrap_reduction_result(self, name: str, result, *, skipna, axis):
|
|
1668
|
+
if isinstance(result, np.ndarray):
|
|
1669
|
+
if skipna:
|
|
1670
|
+
# we only retain mask for all-NA rows/columns
|
|
1671
|
+
mask = self._mask.all(axis=axis)
|
|
1672
|
+
else:
|
|
1673
|
+
mask = self._mask.any(axis=axis)
|
|
1674
|
+
|
|
1675
|
+
return self._maybe_mask_result(result, mask)
|
|
1676
|
+
return result
|
|
1677
|
+
|
|
1678
|
+
def _wrap_na_result(self, *, name, axis, mask_size):
|
|
1679
|
+
mask = np.ones(mask_size, dtype=bool)
|
|
1680
|
+
|
|
1681
|
+
float_dtyp = "float32" if self.dtype == "Float32" else "float64"
|
|
1682
|
+
if name in ["mean", "median", "var", "std", "skew", "kurt", "sem"]:
|
|
1683
|
+
np_dtype = float_dtyp
|
|
1684
|
+
elif name in ["min", "max"] or self.dtype.itemsize == 8:
|
|
1685
|
+
np_dtype = self.dtype.numpy_dtype.name
|
|
1686
|
+
else:
|
|
1687
|
+
is_windows_or_32bit = is_platform_windows() or not IS64
|
|
1688
|
+
int_dtyp = "int32" if is_windows_or_32bit else "int64"
|
|
1689
|
+
uint_dtyp = "uint32" if is_windows_or_32bit else "uint64"
|
|
1690
|
+
np_dtype = {"b": int_dtyp, "i": int_dtyp, "u": uint_dtyp, "f": float_dtyp}[
|
|
1691
|
+
self.dtype.kind
|
|
1692
|
+
]
|
|
1693
|
+
|
|
1694
|
+
value = np.array([1], dtype=np_dtype)
|
|
1695
|
+
return self._maybe_mask_result(value, mask=mask)
|
|
1696
|
+
|
|
1697
|
+
def _wrap_min_count_reduction_result(
|
|
1698
|
+
self, name: str, result, *, skipna, min_count, axis
|
|
1699
|
+
):
|
|
1700
|
+
if min_count == 0 and skipna and isinstance(result, np.ndarray):
|
|
1701
|
+
return self._maybe_mask_result(result, np.zeros(result.shape, dtype=bool))
|
|
1702
|
+
return self._wrap_reduction_result(name, result, skipna=skipna, axis=axis)
|
|
1703
|
+
|
|
1704
|
+
def sum(
|
|
1705
|
+
self,
|
|
1706
|
+
*,
|
|
1707
|
+
skipna: bool = True,
|
|
1708
|
+
min_count: int = 0,
|
|
1709
|
+
axis: AxisInt | None = 0,
|
|
1710
|
+
**kwargs,
|
|
1711
|
+
):
|
|
1712
|
+
nv.validate_sum((), kwargs)
|
|
1713
|
+
|
|
1714
|
+
result = masked_reductions.sum(
|
|
1715
|
+
self._data,
|
|
1716
|
+
self._mask,
|
|
1717
|
+
skipna=skipna,
|
|
1718
|
+
min_count=min_count,
|
|
1719
|
+
axis=axis,
|
|
1720
|
+
)
|
|
1721
|
+
return self._wrap_min_count_reduction_result(
|
|
1722
|
+
"sum", result, skipna=skipna, min_count=min_count, axis=axis
|
|
1723
|
+
)
|
|
1724
|
+
|
|
1725
|
+
def prod(
|
|
1726
|
+
self,
|
|
1727
|
+
*,
|
|
1728
|
+
skipna: bool = True,
|
|
1729
|
+
min_count: int = 0,
|
|
1730
|
+
axis: AxisInt | None = 0,
|
|
1731
|
+
**kwargs,
|
|
1732
|
+
):
|
|
1733
|
+
nv.validate_prod((), kwargs)
|
|
1734
|
+
|
|
1735
|
+
result = masked_reductions.prod(
|
|
1736
|
+
self._data,
|
|
1737
|
+
self._mask,
|
|
1738
|
+
skipna=skipna,
|
|
1739
|
+
min_count=min_count,
|
|
1740
|
+
axis=axis,
|
|
1741
|
+
)
|
|
1742
|
+
return self._wrap_min_count_reduction_result(
|
|
1743
|
+
"prod", result, skipna=skipna, min_count=min_count, axis=axis
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
def mean(self, *, skipna: bool = True, axis: AxisInt | None = 0, **kwargs):
|
|
1747
|
+
nv.validate_mean((), kwargs)
|
|
1748
|
+
result = masked_reductions.mean(
|
|
1749
|
+
self._data,
|
|
1750
|
+
self._mask,
|
|
1751
|
+
skipna=skipna,
|
|
1752
|
+
axis=axis,
|
|
1753
|
+
)
|
|
1754
|
+
return self._wrap_reduction_result("mean", result, skipna=skipna, axis=axis)
|
|
1755
|
+
|
|
1756
|
+
def var(
|
|
1757
|
+
self, *, skipna: bool = True, axis: AxisInt | None = 0, ddof: int = 1, **kwargs
|
|
1758
|
+
):
|
|
1759
|
+
nv.validate_stat_ddof_func((), kwargs, fname="var")
|
|
1760
|
+
result = masked_reductions.var(
|
|
1761
|
+
self._data,
|
|
1762
|
+
self._mask,
|
|
1763
|
+
skipna=skipna,
|
|
1764
|
+
axis=axis,
|
|
1765
|
+
ddof=ddof,
|
|
1766
|
+
)
|
|
1767
|
+
return self._wrap_reduction_result("var", result, skipna=skipna, axis=axis)
|
|
1768
|
+
|
|
1769
|
+
def std(
|
|
1770
|
+
self, *, skipna: bool = True, axis: AxisInt | None = 0, ddof: int = 1, **kwargs
|
|
1771
|
+
):
|
|
1772
|
+
nv.validate_stat_ddof_func((), kwargs, fname="std")
|
|
1773
|
+
result = masked_reductions.std(
|
|
1774
|
+
self._data,
|
|
1775
|
+
self._mask,
|
|
1776
|
+
skipna=skipna,
|
|
1777
|
+
axis=axis,
|
|
1778
|
+
ddof=ddof,
|
|
1779
|
+
)
|
|
1780
|
+
return self._wrap_reduction_result("std", result, skipna=skipna, axis=axis)
|
|
1781
|
+
|
|
1782
|
+
def min(self, *, skipna: bool = True, axis: AxisInt | None = 0, **kwargs):
|
|
1783
|
+
nv.validate_min((), kwargs)
|
|
1784
|
+
result = masked_reductions.min(
|
|
1785
|
+
self._data,
|
|
1786
|
+
self._mask,
|
|
1787
|
+
skipna=skipna,
|
|
1788
|
+
axis=axis,
|
|
1789
|
+
)
|
|
1790
|
+
return self._wrap_reduction_result("min", result, skipna=skipna, axis=axis)
|
|
1791
|
+
|
|
1792
|
+
def max(self, *, skipna: bool = True, axis: AxisInt | None = 0, **kwargs):
|
|
1793
|
+
nv.validate_max((), kwargs)
|
|
1794
|
+
result = masked_reductions.max(
|
|
1795
|
+
self._data,
|
|
1796
|
+
self._mask,
|
|
1797
|
+
skipna=skipna,
|
|
1798
|
+
axis=axis,
|
|
1799
|
+
)
|
|
1800
|
+
return self._wrap_reduction_result("max", result, skipna=skipna, axis=axis)
|
|
1801
|
+
|
|
1802
|
+
def count(self) -> np.int64:
|
|
1803
|
+
return (~self._mask).sum()
|
|
1804
|
+
|
|
1805
|
+
@overload
|
|
1806
|
+
def any(
|
|
1807
|
+
self, *, skipna: Literal[True] = ..., axis: AxisInt | None = ..., **kwargs
|
|
1808
|
+
) -> np.bool_: ...
|
|
1809
|
+
|
|
1810
|
+
@overload
|
|
1811
|
+
def any(
|
|
1812
|
+
self, *, skipna: bool, axis: AxisInt | None = ..., **kwargs
|
|
1813
|
+
) -> np.bool_ | NAType: ...
|
|
1814
|
+
|
|
1815
|
+
def any(
|
|
1816
|
+
self, *, skipna: bool = True, axis: AxisInt | None = 0, **kwargs
|
|
1817
|
+
) -> np.bool_ | NAType:
|
|
1818
|
+
"""
|
|
1819
|
+
Return whether any element is truthy.
|
|
1820
|
+
|
|
1821
|
+
Returns False unless there is at least one element that is truthy.
|
|
1822
|
+
By default, NAs are skipped. If ``skipna=False`` is specified and
|
|
1823
|
+
missing values are present, similar :ref:`Kleene logic <boolean.kleene>`
|
|
1824
|
+
is used as for logical operations.
|
|
1825
|
+
|
|
1826
|
+
Parameters
|
|
1827
|
+
----------
|
|
1828
|
+
skipna : bool, default True
|
|
1829
|
+
Exclude NA values. If the entire array is NA and `skipna` is
|
|
1830
|
+
True, then the result will be False, as for an empty array.
|
|
1831
|
+
If `skipna` is False, the result will still be True if there is
|
|
1832
|
+
at least one element that is truthy, otherwise NA will be returned
|
|
1833
|
+
if there are NA's present.
|
|
1834
|
+
axis : int, optional, default 0
|
|
1835
|
+
**kwargs : any, default None
|
|
1836
|
+
Additional keywords have no effect but might be accepted for
|
|
1837
|
+
compatibility with NumPy.
|
|
1838
|
+
|
|
1839
|
+
Returns
|
|
1840
|
+
-------
|
|
1841
|
+
bool or :attr:`pandas.NA`
|
|
1842
|
+
|
|
1843
|
+
See Also
|
|
1844
|
+
--------
|
|
1845
|
+
numpy.any : Numpy version of this method.
|
|
1846
|
+
BaseMaskedArray.all : Return whether all elements are truthy.
|
|
1847
|
+
|
|
1848
|
+
Examples
|
|
1849
|
+
--------
|
|
1850
|
+
The result indicates whether any element is truthy (and by default
|
|
1851
|
+
skips NAs):
|
|
1852
|
+
|
|
1853
|
+
>>> pd.array([True, False, True]).any()
|
|
1854
|
+
np.True_
|
|
1855
|
+
>>> pd.array([True, False, pd.NA]).any()
|
|
1856
|
+
np.True_
|
|
1857
|
+
>>> pd.array([False, False, pd.NA]).any()
|
|
1858
|
+
np.False_
|
|
1859
|
+
>>> pd.array([], dtype="boolean").any()
|
|
1860
|
+
np.False_
|
|
1861
|
+
>>> pd.array([pd.NA], dtype="boolean").any()
|
|
1862
|
+
np.False_
|
|
1863
|
+
>>> pd.array([pd.NA], dtype="Float64").any()
|
|
1864
|
+
np.False_
|
|
1865
|
+
|
|
1866
|
+
With ``skipna=False``, the result can be NA if this is logically
|
|
1867
|
+
required (whether ``pd.NA`` is True or False influences the result):
|
|
1868
|
+
|
|
1869
|
+
>>> pd.array([True, False, pd.NA]).any(skipna=False)
|
|
1870
|
+
np.True_
|
|
1871
|
+
>>> pd.array([1, 0, pd.NA]).any(skipna=False)
|
|
1872
|
+
np.True_
|
|
1873
|
+
>>> pd.array([False, False, pd.NA]).any(skipna=False)
|
|
1874
|
+
<NA>
|
|
1875
|
+
>>> pd.array([0, 0, pd.NA]).any(skipna=False)
|
|
1876
|
+
<NA>
|
|
1877
|
+
"""
|
|
1878
|
+
nv.validate_any((), kwargs)
|
|
1879
|
+
|
|
1880
|
+
values = self._data.copy()
|
|
1881
|
+
np.putmask(values, self._mask, self.dtype._falsey_value)
|
|
1882
|
+
result = values.any(axis=axis)
|
|
1883
|
+
|
|
1884
|
+
if isinstance(result, np.ndarray):
|
|
1885
|
+
if skipna:
|
|
1886
|
+
mask = np.zeros(result.shape, dtype=bool)
|
|
1887
|
+
else:
|
|
1888
|
+
# Kleene logic: False | NA = NA, True | NA = True
|
|
1889
|
+
mask = ~result & self._mask.any(axis=axis)
|
|
1890
|
+
return self._maybe_mask_result(result, mask)
|
|
1891
|
+
|
|
1892
|
+
if skipna:
|
|
1893
|
+
return result
|
|
1894
|
+
elif result or len(self) == 0 or not self._mask.any():
|
|
1895
|
+
return result
|
|
1896
|
+
else:
|
|
1897
|
+
return self.dtype.na_value
|
|
1898
|
+
|
|
1899
|
+
@overload
|
|
1900
|
+
def all(
|
|
1901
|
+
self, *, skipna: Literal[True] = ..., axis: AxisInt | None = ..., **kwargs
|
|
1902
|
+
) -> np.bool_: ...
|
|
1903
|
+
|
|
1904
|
+
@overload
|
|
1905
|
+
def all(
|
|
1906
|
+
self, *, skipna: bool, axis: AxisInt | None = ..., **kwargs
|
|
1907
|
+
) -> np.bool_ | NAType: ...
|
|
1908
|
+
|
|
1909
|
+
def all(
|
|
1910
|
+
self, *, skipna: bool = True, axis: AxisInt | None = 0, **kwargs
|
|
1911
|
+
) -> np.bool_ | NAType:
|
|
1912
|
+
"""
|
|
1913
|
+
Return whether all elements are truthy.
|
|
1914
|
+
|
|
1915
|
+
Returns True unless there is at least one element that is falsey.
|
|
1916
|
+
By default, NAs are skipped. If ``skipna=False`` is specified and
|
|
1917
|
+
missing values are present, similar :ref:`Kleene logic <boolean.kleene>`
|
|
1918
|
+
is used as for logical operations.
|
|
1919
|
+
|
|
1920
|
+
Parameters
|
|
1921
|
+
----------
|
|
1922
|
+
skipna : bool, default True
|
|
1923
|
+
Exclude NA values. If the entire array is NA and `skipna` is
|
|
1924
|
+
True, then the result will be True, as for an empty array.
|
|
1925
|
+
If `skipna` is False, the result will still be False if there is
|
|
1926
|
+
at least one element that is falsey, otherwise NA will be returned
|
|
1927
|
+
if there are NA's present.
|
|
1928
|
+
axis : int, optional, default 0
|
|
1929
|
+
**kwargs : any, default None
|
|
1930
|
+
Additional keywords have no effect but might be accepted for
|
|
1931
|
+
compatibility with NumPy.
|
|
1932
|
+
|
|
1933
|
+
Returns
|
|
1934
|
+
-------
|
|
1935
|
+
bool or :attr:`pandas.NA`
|
|
1936
|
+
|
|
1937
|
+
See Also
|
|
1938
|
+
--------
|
|
1939
|
+
numpy.all : Numpy version of this method.
|
|
1940
|
+
BooleanArray.any : Return whether any element is truthy.
|
|
1941
|
+
|
|
1942
|
+
Examples
|
|
1943
|
+
--------
|
|
1944
|
+
The result indicates whether all elements are truthy (and by default
|
|
1945
|
+
skips NAs):
|
|
1946
|
+
|
|
1947
|
+
>>> pd.array([True, True, pd.NA]).all()
|
|
1948
|
+
np.True_
|
|
1949
|
+
>>> pd.array([1, 1, pd.NA]).all()
|
|
1950
|
+
np.True_
|
|
1951
|
+
>>> pd.array([True, False, pd.NA]).all()
|
|
1952
|
+
np.False_
|
|
1953
|
+
>>> pd.array([], dtype="boolean").all()
|
|
1954
|
+
np.True_
|
|
1955
|
+
>>> pd.array([pd.NA], dtype="boolean").all()
|
|
1956
|
+
np.True_
|
|
1957
|
+
>>> pd.array([pd.NA], dtype="Float64").all()
|
|
1958
|
+
np.True_
|
|
1959
|
+
|
|
1960
|
+
With ``skipna=False``, the result can be NA if this is logically
|
|
1961
|
+
required (whether ``pd.NA`` is True or False influences the result):
|
|
1962
|
+
|
|
1963
|
+
>>> pd.array([True, True, pd.NA]).all(skipna=False)
|
|
1964
|
+
<NA>
|
|
1965
|
+
>>> pd.array([1, 1, pd.NA]).all(skipna=False)
|
|
1966
|
+
<NA>
|
|
1967
|
+
>>> pd.array([True, False, pd.NA]).all(skipna=False)
|
|
1968
|
+
np.False_
|
|
1969
|
+
>>> pd.array([1, 0, pd.NA]).all(skipna=False)
|
|
1970
|
+
np.False_
|
|
1971
|
+
"""
|
|
1972
|
+
nv.validate_all((), kwargs)
|
|
1973
|
+
|
|
1974
|
+
values = self._data.copy()
|
|
1975
|
+
np.putmask(values, self._mask, self.dtype._truthy_value)
|
|
1976
|
+
result = values.all(axis=axis)
|
|
1977
|
+
|
|
1978
|
+
if isinstance(result, np.ndarray):
|
|
1979
|
+
if skipna:
|
|
1980
|
+
mask = np.zeros(result.shape, dtype=bool)
|
|
1981
|
+
else:
|
|
1982
|
+
# Kleene logic: True & NA = NA, False & NA = False
|
|
1983
|
+
mask = result & self._mask.any(axis=axis)
|
|
1984
|
+
return self._maybe_mask_result(result, mask)
|
|
1985
|
+
|
|
1986
|
+
if skipna:
|
|
1987
|
+
return result
|
|
1988
|
+
elif not result or len(self) == 0 or not self._mask.any():
|
|
1989
|
+
return result
|
|
1990
|
+
else:
|
|
1991
|
+
return self.dtype.na_value
|
|
1992
|
+
|
|
1993
|
+
def interpolate(
|
|
1994
|
+
self,
|
|
1995
|
+
*,
|
|
1996
|
+
method: InterpolateOptions,
|
|
1997
|
+
axis: int,
|
|
1998
|
+
index,
|
|
1999
|
+
limit,
|
|
2000
|
+
limit_direction,
|
|
2001
|
+
limit_area,
|
|
2002
|
+
copy: bool,
|
|
2003
|
+
**kwargs,
|
|
2004
|
+
) -> FloatingArray:
|
|
2005
|
+
"""
|
|
2006
|
+
See NDFrame.interpolate.__doc__.
|
|
2007
|
+
"""
|
|
2008
|
+
# NB: we return type(self) even if copy=False
|
|
2009
|
+
if self.dtype.kind == "f":
|
|
2010
|
+
if copy:
|
|
2011
|
+
data = self._data.copy()
|
|
2012
|
+
mask = self._mask.copy()
|
|
2013
|
+
else:
|
|
2014
|
+
data = self._data
|
|
2015
|
+
mask = self._mask
|
|
2016
|
+
elif self.dtype.kind in "iu":
|
|
2017
|
+
copy = True
|
|
2018
|
+
data = self._data.astype("f8")
|
|
2019
|
+
mask = self._mask.copy()
|
|
2020
|
+
else:
|
|
2021
|
+
raise NotImplementedError(
|
|
2022
|
+
f"interpolate is not implemented for dtype={self.dtype}"
|
|
2023
|
+
)
|
|
2024
|
+
|
|
2025
|
+
missing.interpolate_2d_inplace(
|
|
2026
|
+
data,
|
|
2027
|
+
method=method,
|
|
2028
|
+
axis=0,
|
|
2029
|
+
index=index,
|
|
2030
|
+
limit=limit,
|
|
2031
|
+
limit_direction=limit_direction,
|
|
2032
|
+
limit_area=limit_area,
|
|
2033
|
+
mask=mask,
|
|
2034
|
+
**kwargs,
|
|
2035
|
+
)
|
|
2036
|
+
if not copy:
|
|
2037
|
+
return self # type: ignore[return-value]
|
|
2038
|
+
if self.dtype.kind == "f":
|
|
2039
|
+
return type(self)._simple_new(data, mask) # type: ignore[return-value]
|
|
2040
|
+
else:
|
|
2041
|
+
from pandas.core.arrays import FloatingArray
|
|
2042
|
+
|
|
2043
|
+
return FloatingArray._simple_new(data, mask)
|
|
2044
|
+
|
|
2045
|
+
def _accumulate(
|
|
2046
|
+
self, name: str, *, skipna: bool = True, **kwargs
|
|
2047
|
+
) -> BaseMaskedArray:
|
|
2048
|
+
data = self._data
|
|
2049
|
+
mask = self._mask
|
|
2050
|
+
|
|
2051
|
+
op = getattr(masked_accumulations, name)
|
|
2052
|
+
data, mask = op(data, mask, skipna=skipna, **kwargs)
|
|
2053
|
+
|
|
2054
|
+
return self._simple_new(data, mask)
|
|
2055
|
+
|
|
2056
|
+
# ------------------------------------------------------------------
|
|
2057
|
+
# GroupBy Methods
|
|
2058
|
+
|
|
2059
|
+
def _groupby_op(
|
|
2060
|
+
self,
|
|
2061
|
+
*,
|
|
2062
|
+
how: str,
|
|
2063
|
+
has_dropped_na: bool,
|
|
2064
|
+
min_count: int,
|
|
2065
|
+
ngroups: int,
|
|
2066
|
+
ids: npt.NDArray[np.intp],
|
|
2067
|
+
**kwargs,
|
|
2068
|
+
):
|
|
2069
|
+
from pandas.core.groupby.ops import WrappedCythonOp
|
|
2070
|
+
|
|
2071
|
+
kind = WrappedCythonOp.get_kind_from_how(how)
|
|
2072
|
+
op = WrappedCythonOp(how=how, kind=kind, has_dropped_na=has_dropped_na)
|
|
2073
|
+
|
|
2074
|
+
# libgroupby functions are responsible for NOT altering mask
|
|
2075
|
+
mask = self._mask
|
|
2076
|
+
if op.kind != "aggregate":
|
|
2077
|
+
result_mask = mask.copy()
|
|
2078
|
+
else:
|
|
2079
|
+
result_mask = np.zeros(ngroups, dtype=bool)
|
|
2080
|
+
|
|
2081
|
+
if how == "rank" and kwargs.get("na_option") in ["top", "bottom"]:
|
|
2082
|
+
result_mask[:] = False
|
|
2083
|
+
|
|
2084
|
+
res_values = op._cython_op_ndim_compat(
|
|
2085
|
+
self._data,
|
|
2086
|
+
min_count=min_count,
|
|
2087
|
+
ngroups=ngroups,
|
|
2088
|
+
comp_ids=ids,
|
|
2089
|
+
mask=mask,
|
|
2090
|
+
result_mask=result_mask,
|
|
2091
|
+
**kwargs,
|
|
2092
|
+
)
|
|
2093
|
+
|
|
2094
|
+
if op.how == "ohlc":
|
|
2095
|
+
arity = op._cython_arity.get(op.how, 1)
|
|
2096
|
+
result_mask = np.tile(result_mask, (arity, 1)).T
|
|
2097
|
+
|
|
2098
|
+
if op.how in ["idxmin", "idxmax"]:
|
|
2099
|
+
# Result values are indexes to take, keep as ndarray
|
|
2100
|
+
return res_values
|
|
2101
|
+
else:
|
|
2102
|
+
# res_values should already have the correct dtype, we just need to
|
|
2103
|
+
# wrap in a MaskedArray
|
|
2104
|
+
return self._maybe_mask_result(res_values, result_mask)
|
|
2105
|
+
|
|
2106
|
+
|
|
2107
|
+
def transpose_homogeneous_masked_arrays(
|
|
2108
|
+
masked_arrays: Sequence[BaseMaskedArray],
|
|
2109
|
+
) -> list[BaseMaskedArray]:
|
|
2110
|
+
"""Transpose masked arrays in a list, but faster.
|
|
2111
|
+
|
|
2112
|
+
Input should be a list of 1-dim masked arrays of equal length and all have the
|
|
2113
|
+
same dtype. The caller is responsible for ensuring validity of input data.
|
|
2114
|
+
"""
|
|
2115
|
+
masked_arrays = list(masked_arrays)
|
|
2116
|
+
dtype = masked_arrays[0].dtype
|
|
2117
|
+
|
|
2118
|
+
values = [arr._data.reshape(1, -1) for arr in masked_arrays]
|
|
2119
|
+
transposed_values = np.concatenate(
|
|
2120
|
+
values,
|
|
2121
|
+
axis=0,
|
|
2122
|
+
out=np.empty(
|
|
2123
|
+
(len(masked_arrays), len(masked_arrays[0])),
|
|
2124
|
+
order="F",
|
|
2125
|
+
dtype=dtype.numpy_dtype,
|
|
2126
|
+
),
|
|
2127
|
+
)
|
|
2128
|
+
|
|
2129
|
+
masks = [arr._mask.reshape(1, -1) for arr in masked_arrays]
|
|
2130
|
+
transposed_masks = np.concatenate(
|
|
2131
|
+
masks, axis=0, out=np.empty_like(transposed_values, dtype=bool)
|
|
2132
|
+
)
|
|
2133
|
+
|
|
2134
|
+
arr_type = dtype.construct_array_type()
|
|
2135
|
+
transposed_arrays: list[BaseMaskedArray] = []
|
|
2136
|
+
for i in range(transposed_values.shape[1]):
|
|
2137
|
+
transposed_arr = arr_type(transposed_values[:, i], mask=transposed_masks[:, i])
|
|
2138
|
+
transposed_arrays.append(transposed_arr)
|
|
2139
|
+
|
|
2140
|
+
return transposed_arrays
|