Title: Writing DataFrame-Agnostic Python Code With Narwhals – Real Python
Open Graph Title: Writing DataFrame-Agnostic Python Code With Narwhals – Real Python
Description: If you're a Python library developer looking to write DataFrame-agnostic code, this tutorial will show how the Narwhals library could give you a solution.
Open Graph Description: If you're a Python library developer looking to write DataFrame-agnostic code, this tutorial will show how the Narwhals library could give you a solution.
Mail addresses
?subject=Python article for you&body=Writing DataFrame-Agnostic Python Code With Narwhals on Real Python
https://realpython.com/narwhals-python/
Opengraph URL: https://realpython.com/narwhals-python/
X: @realpython
Domain: realpython.com
{
"@context": "http://schema.org",
"@type": "Article",
"headline": "Writing DataFrame-Agnostic Python Code With Narwhals",
"image": {
"@type": "ImageObject",
"url": "https://files.realpython.com/media/Narwhals-Python-DataFrameInteroperability-for-Pandas-Polars--More_Watermarked.ff492f3b2207.jpg",
"width": 1920,
"height": 1080
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://realpython.com/narwhals-python/",
"lastReviewed": "2025-10-31",
"author": {
"@type": "Person",
"name": "Ian Eyre",
"image": "https://realpython.com/cdn-cgi/image/width=644,height=644,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/Me_at_Graceland.f88418f34d62.fa6f5ab743da.png",
"url": "https://realpython.com/team/ieyre/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
},
"reviewedBy": [
{
"@type": "Person",
"name": "Aldren Santos",
"image": "https://realpython.com/cdn-cgi/image/width=500,height=500,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/Aldren_Santos_Real_Python.6b0861d8b841.png",
"url": "https://realpython.com/team/asantos/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
},
{
"@type": "Person",
"name": "Brenda Weleschuk",
"image": "https://realpython.com/cdn-cgi/image/width=320,height=320,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/IMG_3324_1.50b309355fc1.jpg",
"url": "https://realpython.com/team/bweleschuk/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
},
{
"@type": "Person",
"name": "Bartosz Zaczy\u0144ski",
"image": "https://realpython.com/cdn-cgi/image/width=1694,height=1694,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/coders_lab_2109368.259b1599fbee.jpg",
"url": "https://realpython.com/team/bzaczynski/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
},
{
"@type": "Person",
"name": "Kate Finegan",
"image": "https://realpython.com/cdn-cgi/image/width=400,height=400,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/VZxEtUor_400x400.7169c68e3950.jpg",
"url": "https://realpython.com/team/kfinegan/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
}
]
},
"datePublished": "2025-12-15T14:00:00+00:00",
"dateModified": "2025-10-31T12:30:19.001795+00:00",
"publisher": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": {
"@type": "ImageObject",
"url": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png",
"width": 512,
"height": 512
},
"description": "Real Python is a leading provider of online Python education and one of the largest language-specific online communities for software developers. It publishes high-quality learning resources, such as tutorials, books, and courses to an audience of millions of developers, data scientists, and machine learning engineers each month.",
"slogan": "Become a Python Expert",
"email": "info@realpython.com",
"sameAs": [
"https://github.com/realpython",
"https://www.youtube.com/realpython",
"https://twitter.com/realpython",
"https://x.com/realpython",
"https://www.linkedin.com/company/realpython-com/",
"https://www.facebook.com/learnrealpython",
"https://www.instagram.com/realpython",
"https://www.tiktok.com/@realpython.com"
]
},
"author": {
"@type": "Person",
"name": "Ian Eyre",
"image": "https://realpython.com/cdn-cgi/image/width=644,height=644,fit=crop,gravity=auto,format=auto/https://files.realpython.com/media/Me_at_Graceland.f88418f34d62.fa6f5ab743da.png",
"url": "https://realpython.com/team/ieyre/",
"affiliation": {
"@type": "Organization",
"@id": "https://realpython.com/#organization",
"name": "Real Python",
"url": "https://realpython.com",
"logo": "https://realpython.com/static/real-python-logo-square-512.157ae6bf64ed.png"
}
},
"description": "If you're a Python library developer looking to write DataFrame-agnostic code, this tutorial will show how the Narwhals library could give you a solution.",
"hasPart": {
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What problem does Narwhals solve, and who should use it?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Narwhals lets library authors write DataFrame-agnostic code that runs against pandas, Polars, DuckDB, and more with the same API. It’s aimed at Python library developers who need one code path to analyze common DataFrame formats.
"
}
},
{
"@type": "Question",
"name": "Does Narwhals replace pandas or Polars?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Narwhals wraps the input and translates your expressions, then hands execution to the original library. It complements pandas and Polars rather than replacing them.
"
}
},
{
"@type": "Question",
"name": "Does Narwhals copy my data or slow things down?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Narwhals avoids copying by operating on the original DataFrame and delegating work to the source backend. This keeps memory use low and preserves the performance characteristics of the underlying library.
"
}
},
{
"@type": "Question",
"name": "How do I control the input and output types with Narwhals?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Wrap inputs with nw.from_native() and return the original type with .to_native(). If you always want a specific output, call .to_pandas() or .to_polars() instead, or use the @nw.narwhalify decorator to auto-handle the conversions around your function body.
"
}
},
{
"@type": "Question",
"name": "What should I know about eager vs lazy use?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Narwhals mirrors Polars with nw.DataFrame for eager and nw.LazyFrame for lazy execution. Some operations like .pivot() require materialized data, so convert a lazy input with .collect() before those steps.
"
}
}
]
}
}
| author | Real Python |
| twitter:card | summary_large_image |
| twitter:image | https://files.realpython.com/media/Narwhals-Python-DataFrameInteroperability-for-Pandas-Polars--More_Watermarked.ff492f3b2207.jpg |
| og:image | https://files.realpython.com/media/Narwhals-Python-DataFrameInteroperability-for-Pandas-Polars--More_Watermarked.ff492f3b2207.jpg |
| twitter:creator | @realpython |
| og:type | article |
Links:
Viewport: width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover
Robots: max-image-preview:large