{"id":538,"library":"narwhals","title":"Narwhals","description":"Narwhals is an extremely lightweight and extensible compatibility layer between dataframe libraries. It provides a unified API, largely mirroring the Polars API, enabling users to write dataframe-agnostic code that works across various backends such as pandas, Polars, cuDF, PyArrow, Dask, DuckDB, Ibis, PySpark, and SQLFrame. It is currently at version 2.18.1 and maintains an active development cycle with frequent releases, often including weekly or bi-weekly updates for bug fixes and minor enhancements.","status":"active","version":"2.18.1","language":"python","source_language":"en","source_url":"https://github.com/narwhals-dev/narwhals","tags":["dataframes","dataframe-api","polars","pandas","cudf","pyarrow","dask","duckdb","data-science"],"install":[{"cmd":"pip install narwhals","lang":"bash","label":"Install Narwhals"}],"dependencies":[{"reason":"Common backend for Narwhals compatibility","package":"pandas","optional":true},{"reason":"Primary API inspiration and common backend for Narwhals compatibility","package":"polars","optional":true},{"reason":"Common backend for Narwhals compatibility","package":"pyarrow","optional":true}],"imports":[{"symbol":"narwhals","correct":"import narwhals as nw"},{"note":"Useful for type hinting dataframe-agnostic functions","symbol":"IntoFrameT","correct":"from narwhals.typing import IntoFrameT"},{"note":"Use this for long-term API stability in library development, though the main 'narwhals' namespace is more actively developed for new features.","symbol":"stable.v1","correct":"import narwhals.stable.v1 as nw_stable"}],"quickstart":{"code":"import narwhals as nw\nimport pandas as pd\nimport polars as pl\nfrom narwhals.typing import IntoFrameT\n\ndef process_data(df_native: IntoFrameT) -> IntoFrameT:\n    df = nw.from_native(df_native)\n    result = (\n        df.group_by(nw.col('category'))\n        .agg(nw.col('value').mean().alias('mean_value'))\n        .sort('mean_value', descending=True)\n    )\n    return result.to_native()\n\n# Example with pandas\npd_df = pd.DataFrame({'category': ['A', 'B', 'A', 'C'], 'value': [10, 20, 15, 25]})\npd_result = process_data(pd_df)\nprint('Pandas Result:')\nprint(pd_result)\n\n# Example with polars\npl_df = pl.DataFrame({'category': ['A', 'B', 'A', 'C'], 'value': [10, 20, 15, 25]})\npl_result = process_data(pl_df)\nprint('\\nPolars Result:')\nprint(pl_result)","lang":"python","description":"This quickstart demonstrates how to write a dataframe-agnostic function using Narwhals. The function `process_data` accepts any supported native dataframe type (e.g., pandas DataFrame, Polars DataFrame) via `nw.from_native`, applies a group-by and aggregation operation using Narwhals' Polars-like API, and then converts the result back to the original native dataframe type using `to_native()`."},"warnings":[{"fix":"Install the desired backend libraries, e.g., `pip install pandas polars pyarrow`.","message":"Narwhals is a compatibility layer and does not provide dataframe functionality itself. You must install the underlying dataframe libraries (e.g., `pandas`, `polars`, `pyarrow`) separately for Narwhals to function with those backends. Not installing them will lead to `ModuleNotFoundError` or `TypeError` when `from_native` is used.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For production code or libraries, use `import narwhals.stable.v1 as nw` and adhere to its API. For experimentation, the main `narwhals` namespace is acceptable.","message":"The main `narwhals` namespace may undergo breaking changes, deprecations, or API shifts in new releases. For critical library development requiring long-term stability, prefer `import narwhals.stable.v1 as nw_stable`. This stable API is promised to never change or remove public functions. Future stable versions (e.g., `v2`, `v3`) will be introduced if breaking changes are necessary.","severity":"breaking","affected_versions":"All versions, for code not using `narwhals.stable.v1`"},{"fix":"Refer to the Narwhals documentation for supported API elements. If a specific Polars feature is critical and not supported, you may need to handle that backend natively or contribute to Narwhals.","message":"Narwhals implements a *subset* of the Polars API. Not all Polars functions, arguments, or behaviors are necessarily supported or identically replicated across all backends. Complex or less common Polars operations might not be available or might behave differently in specific backend implementations. Always consult the official Narwhals API completeness documentation.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Identify points in your code where eager computation is implicitly or explicitly needed and call `.collect()` on your Narwhals LazyFrame. Alternatively, use `eager_only=True` in `nw.from_native` if you always expect an eager frame.","message":"Narwhals preserves the eager/lazy execution model of the underlying dataframe. If you pass a lazy frame (e.g., Polars LazyFrame, Dask DataFrame), operations remain lazy. Explicitly call `.collect()` when an eager result is required, especially before operations like `.shape` or `.pivot()`, which necessitate materializing the data. Failing to do so can lead to errors or unexpected behavior.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Keep Narwhals updated to benefit from fixes addressing upstream deprecations and inconsistencies. Thoroughly test your code across different backend versions and with various null value scenarios.","message":"External library deprecations can affect Narwhals. For example, DuckDB 1.5 deprecated `fetch_arrow_table`. While Narwhals strives to adapt, relying on specific backend versions might expose you to these upstream changes. This is particularly relevant for `when/then` conditions, `join` operations, and null value handling across different backends, which have seen several fixes in recent versions.","severity":"deprecated","affected_versions":"V2.10.2 onwards (intermittent fixes)"}],"env_vars":null,"last_verified":"2026-05-12T14:47:15.406Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Install the library using pip: `pip install narwhals`","cause":"The `narwhals` library is not installed in your Python environment.","error":"ModuleNotFoundError: No module named 'narwhals'"},{"fix":"Verify the column name for typos or confirm its existence using `df.columns` or `df.collect().columns` (for LazyFrames) to inspect available columns.","cause":"An operation was attempted on a column name that does not exist in the DataFrame or LazyFrame.","error":"narwhals.exceptions.ColumnNotFoundError: Column 'column_name' not found."},{"fix":"Wrap literal values with `nw.lit()` (e.g., `nw.lit(0)` instead of `0`) and select columns by index or non-string names with `nw.col()` (e.g., `nw.col(0)` instead of `0`).","cause":"Narwhals' expression-based API requires explicit wrapping of literal values (e.g., integers, strings) with `nw.lit()` or column selections by index with `nw.col()` in operations like `select` or `with_columns`.","error":"TypeError: Expected an object which can be converted into an expression, got <class 'int'>"},{"fix":"Refer to the Narwhals documentation for supported API features. If the functionality is critical and unavailable, convert to the native backend using `.to_native()` to perform the operation, and then wrap the result back with `nw.from_native()` if further Narwhals operations are needed.","cause":"Narwhals implements a subset of the Polars API. The specific method or attribute being called exists in the native Polars library but is not currently exposed or supported by the Narwhals compatibility layer.","error":"AttributeError: 'DataFrame' object has no attribute 'some_polars_method'"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":5.4,"disk_size":"22.2M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.4,"disk_size":"23M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.21,"mem_mb":5.8,"disk_size":"24.8M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.24,"mem_mb":5.8,"disk_size":"25M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.17,"mem_mb":5.8,"disk_size":"16.5M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.24,"mem_mb":5.8,"disk_size":"17M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.15,"mem_mb":5.9,"disk_size":"16.1M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.2,"mem_mb":5.9,"disk_size":"17M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":5.7,"disk_size":"21.7M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":5.7,"disk_size":"22M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}