{"id":606504,"date":"2025-07-07T12:05:53","date_gmt":"2025-07-07T17:05:53","guid":{"rendered":"https:\/\/towardsdatascience.com\/?p=606504"},"modified":"2025-07-07T12:06:08","modified_gmt":"2025-07-07T17:06:08","slug":"liberating-performance-with-immutable-dataframes-in-free-threaded-python","status":"publish","type":"post","link":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/","title":{"rendered":"Liberating Performance with Immutable DataFrames in Free-Threaded Python"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><mdspan datatext=\"el1751675262572\" class=\"mdspan-comment\">Applying a function<\/mdspan> to each row of a DataFrame is a common operation. These operations are embarrassingly parallel: each row can be processed independently. With a multi-core CPU, many rows can be processed at once.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Until recently, exploiting this opportunity in Python was not possible. Multi-threaded function application, being CPU-bound, was throttled by the Global Interpreter Lock (GIL).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Python now offers a solution: with the &#8220;experimental free-threading build&#8221; of Python 3.13, the GIL is removed, and true multi-threaded concurrency of CPU-bound operations is possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The performance benefits are extraordinary. Leveraging free-threaded Python, <a href=\"https:\/\/github.com\/static-frame\/static-frame\">StaticFrame<\/a> 3.2 can perform row-wise function application on a DataFrame at least twice as fast as single-threaded execution.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, for each row of a square DataFrame of one-million integers, we can calculate the sum of all even values with <code>lambda s: s.loc[s % 2 == 0].sum()<\/code>. When using Python 3.13t (the &#8220;t&#8221; denotes the free-threaded variant), the duration (measured with <code>ipython<\/code> <code>%timeit<\/code>) drops by more than 60%, from 21.3 ms to 7.89 ms:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\"># Python 3.13.5 experimental free-threading build (main, Jun 11 2025, 15:36:57) [Clang 16.0.0 (clang-1600.0.26.6)] on darwin\n&gt;&gt;&gt; import numpy as np; import static_frame as sf\n\n&gt;&gt;&gt; f = sf.Frame(np.arange(1_000_000).reshape(1000, 1000))\n&gt;&gt;&gt; func = lambda s: s.loc[s % 2 == 0].sum()\n\n&gt;&gt;&gt; %timeit f.iter_series(axis=1).apply(func)\n21.3 ms \u00b1 77.1 \u03bcs per loop (mean \u00b1 std. dev. of 7 runs, 10 loops each)\n\n&gt;&gt;&gt; %timeit f.iter_series(axis=1).apply_pool(func, use_threads=True, max_workers=4)\n7.89 ms \u00b1 60.1 \u03bcs per loop (mean \u00b1 std. dev. of 7 runs, 100 loops each)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Row-wise function application in StaticFrame uses the <code>iter_series(axis=1)<\/code> interface followed by either <code>apply()<\/code> (for single-threaded application) or <code>apply_pool()<\/code> for multi-threaded (<code>use_threads=True<\/code>) or multi-processed (<code>use_threads=False<\/code>) application.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The benefits of using free-threaded Python are robust: the outperformance is consistent across a wide range of DataFrame shapes and compositions, is proportional in both MacOS and Linux, and positively scales with DataFrame size.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When using standard Python with the GIL enabled, multi-threaded processing of CPU-bound processes often degrades performance. As shown below, the duration of the same operation in standard Python increases from 17.7 ms with a single thread to almost 40 ms with multi-threading:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\"># Python 3.13.5 (main, Jun 11 2025, 15:36:57) [Clang 16.0.0 (clang-1600.0.26.6)]\n&gt;&gt;&gt; import numpy as np; import static_frame as sf\n\n&gt;&gt;&gt; f = sf.Frame(np.arange(1_000_000).reshape(1000, 1000))\n&gt;&gt;&gt; func = lambda s: s.loc[s % 2 == 0].sum()\n\n&gt;&gt;&gt; %timeit f.iter_series(axis=1).apply(func)\n17.7 ms \u00b1 144 \u00b5s per loop (mean \u00b1 std. dev. of 7 runs, 100 loops each)\n\n&gt;&gt;&gt; %timeit f.iter_series(axis=1).apply_pool(func, use_threads=True, max_workers=4)\n39.9 ms \u00b1 354 \u00b5s per loop (mean \u00b1 std. dev. of 7 runs, 10 loops each)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There are trade-offs when using free-threaded Python: as apparent in these examples, single-threaded processing is slower (21.3 ms on 3.13t compared to 17.7 ms on 3.13). Free-threaded Python, in general, incurs performance overhead. This is an active area of CPython development and improvements are expected in 3.14t and beyond.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Further, while many C-extension packages like NumPy now offer pre-compiled binary wheels for 3.13t, risks such as thread contention or data races still exists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">StaticFrame avoids these risks by enforcing immutability: thread safety is implicit, eliminating the need for locks or defensive copies. StaticFrame does this by using immutable NumPy arrays (with <code>flags.writeable<\/code> set to <code>False<\/code>) and forbidding in-place mutation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Extended DataFrame Performance Tests<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Evaluating performance characteristics of a complex data structure like a DataFrame requires testing many types of DataFrames. The following performance panels perform row-wise function application on nine different DataFrame types, testing all combinations of three shapes and three levels of type homogeneity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a fixed number of elements (e.g., 1 million), three shapes are tested: tall (10,000 by 100), square (1,000 by 1,000), and wide (100 by 10,0000). To vary type homogeneity, three categories of synthetic data are defined: columnar (no adjacent columns have the same type), mixed (groups of four adjacent columns share the same type), and uniform (all columns are the same type). StaticFrame permits adjacent columns of the same type to be represented as two-dimensional NumPy arrays, reducing the costs of column transversal and row formation. At the uniform extreme, an entire DataFrame can be represented by one two-dimensional array. Synthetic data is produced with the <a href=\"https:\/\/github.com\/static-frame\/frame-fixtures\">frame-fixtures<\/a> package.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The same function is used: <code>lambda s: s.loc[s % 2 == 0].sum()<\/code>. While a more efficient implementation is possible using NumPy directly, this function approximates common applications where many intermediate <code>Series<\/code> are created.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Figure legends document concurrency configuration. When <code>use_threads=True<\/code>, multi-threading is used; when <code>use_threads=False<\/code>, multi-processing is used. StaticFrame uses the <code>ThreadPoolExecutor<\/code> and <code>ProcessPoolExecutor<\/code> interfaces from the standard library and exposes their parameters: the <code>max_workers<\/code> parameter defines the maximum number of threads or processes used. A <code>chunksize<\/code> parameter is also available, but is not varied in this study.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-Threaded Function Application with Free-Threaded Python 3.13t<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">As shown below, the performance benefits of multi-threaded processing in 3.13t are consistent across all DataFrame types tested: processing time is reduced by at least 50%, and in some cases by over 80%. The optimal number of threads (the <code>max_workers<\/code> parameter) is smaller for tall DataFrames, as the quicker processing of smaller rows means that additional thread overhead actually degrades performance.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/threads-ftp-1e6-macos-1024x652.png\" alt=\"\" class=\"wp-image-607548\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Scaling to DataFrames of 100 million elements (1e8), outperformance improves. Processing time is reduced by over 70% for all but two DataFrame types.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/threads-ftp-1e8-macos-1024x652.png\" alt=\"\" class=\"wp-image-607549\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The overhead of multi-threading can vary greatly between platforms. In all cases, the outperformance of using free-threaded Python is proportionally consistent between MacOS and Linux, though MacOS shows marginally greater benefits. The processing of 100 million elements on Linux shows similar relative outperformance:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/threads-ftp-1e8-linux-1024x652.png\" alt=\"\" class=\"wp-image-607550\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Surprisingly, even small DataFrames of only ten-thousand elements (1e4) can benefit from multi-threaded processing in 3.13t. While no benefit is found for wide DataFrames, the processing time of tall and square DataFrames can be reduced in half.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/threads-ftp-1e4-macos-1024x652.png\" alt=\"\" class=\"wp-image-607551\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-Threaded Function Application with Standard Python 3.13<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prior to free-threaded Python, multi-threaded processing of CPU-bound applications resulted in degraded performance. This is made clear below, where the same tests are conducted with standard Python 3.13.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/threads-np-1e6-linux-1024x652.png\" alt=\"\" class=\"wp-image-607552\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Multi-Processed Function Application with Standard Python 3.13<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prior to free-threaded Python, multi-processing was the only option for CPU-bound concurrency. Multi-processing, however, only delivered benefits if the amount of per-process work was sufficient to offset the high cost of creating an interpreter per process and copying data between processes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As shown here, multi-processing row-wise function application significantly degrades performance, process time increasing from two to ten times the single-threaded duration. Each unit of work is too small to make up for multi-processing overhead.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/contributor.insightmediagroup.io\/wp-content\/uploads\/2025\/07\/process-np-1e6-macos-1024x652.png\" alt=\"\" class=\"wp-image-607553\"\/><figcaption class=\"wp-element-caption\">Figure by Author.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The Status of Free-Threaded Python<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/peps.python.org\/pep-0703\">PEP 703<\/a>, &#8220;Making the Global Interpreter Lock Optional in CPython&#8221;, was accepted by the Python Steering Council in July of 2023 with the guidance that, in the first phase (for Python 3.13) it is experimental and non-default; in the second phase, it becomes non-experimental and officially supported; in the third phase, it becomes the default Python implementation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After significant CPython development, and support by critical packages like NumPy, <a href=\"https:\/\/peps.python.org\/pep-0779\">PEP 779<\/a>, &#8220;Criteria for supported status for free-threaded Python&#8221; was accepted by the Python Steering Council in June of 2025. In Python 3.14, free-threaded Python will enter the second phase: non-experimental and officially supported. While it is not yet certain when free-threaded Python will become the default, it is clear that a trajectory is set.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Row-wise function application is just the beginning: group-by operations, windowed function application, and many other operations on immutable DataFrames are similarly well-suited to concurrent execution and are likely to show comparable performance gains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The work to make CPython faster has had success: Python 3.14 is said to be 20% to 40% faster than Python 3.10. Unfortunately, those performance benefits have not been realized for many working with DataFrames, where performance is largely bound within C-extensions (be it NumPy, Arrow, or other libraries).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As shown here, free-threaded Python enables efficient parallel execution using low-cost, memory-efficient threads, delivering a 50% to 90% reduction in processing time, even when performance is primarily bound in C-extension libraries like NumPy. With the ability to safely share immutable data structures across threads, opportunities for substantial performance improvements are now abundant.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How StaticFrame and Python 3.13t enable true thread-based concurrency<\/p>\n","protected":false},"author":18,"featured_media":606505,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"is_member_only":false,"sub_heading":"How StaticFrame and Python 3.13t enable true thread-based concurrency","footnotes":""},"categories":[44],"tags":[10164,3698,3434,491,467],"sponsor":[],"coauthors":[28280],"class_list":["post-606504","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-science","tag-dataframe","tag-immutability","tag-multithreading","tag-programming","tag-python"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science\" \/>\n<meta property=\"og:description\" content=\"How StaticFrame and Python 3.13t enable true thread-based concurrency\" \/>\n<meta property=\"og:url\" content=\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\" \/>\n<meta property=\"og:site_name\" content=\"Towards Data Science\" \/>\n<meta property=\"article:published_time\" content=\"2025-07-07T17:05:53+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-07T17:06:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/default-image.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"675\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Christopher Ariza\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@TDataScience\" \/>\n<meta name=\"twitter:site\" content=\"@TDataScience\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Christopher Ariza\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\"},\"author\":{\"name\":\"TDS Editors\",\"@id\":\"https:\/\/towardsdatascience.com\/#\/schema\/person\/f9925d336b6fe962b03ad8281d90b8ee\"},\"headline\":\"Liberating Performance with Immutable DataFrames in Free-Threaded Python\",\"datePublished\":\"2025-07-07T17:05:53+00:00\",\"dateModified\":\"2025-07-07T17:06:08+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\"},\"wordCount\":1129,\"publisher\":{\"@id\":\"https:\/\/towardsdatascience.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg\",\"keywords\":[\"Dataframe\",\"Immutability\",\"Multithreading\",\"Programming\",\"Python\"],\"articleSection\":[\"Data Science\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\",\"url\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\",\"name\":\"Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science\",\"isPartOf\":{\"@id\":\"https:\/\/towardsdatascience.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg\",\"datePublished\":\"2025-07-07T17:05:53+00:00\",\"dateModified\":\"2025-07-07T17:06:08+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage\",\"url\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg\",\"contentUrl\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg\",\"width\":2560,\"height\":1920,\"caption\":\"Photo by Author.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/towardsdatascience.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Liberating Performance with Immutable DataFrames in Free-Threaded Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/towardsdatascience.com\/#website\",\"url\":\"https:\/\/towardsdatascience.com\/\",\"name\":\"Towards Data Science\",\"description\":\"Publish AI, ML &amp; data-science insights to a global community of data professionals.\",\"publisher\":{\"@id\":\"https:\/\/towardsdatascience.com\/#organization\"},\"alternateName\":\"TDS\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/towardsdatascience.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/towardsdatascience.com\/#organization\",\"name\":\"Towards Data Science\",\"alternateName\":\"TDS\",\"url\":\"https:\/\/towardsdatascience.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/towardsdatascience.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/tds-logo.jpg\",\"contentUrl\":\"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/tds-logo.jpg\",\"width\":696,\"height\":696,\"caption\":\"Towards Data Science\"},\"image\":{\"@id\":\"https:\/\/towardsdatascience.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/TDataScience\",\"https:\/\/www.youtube.com\/c\/TowardsDataScience\",\"https:\/\/www.linkedin.com\/company\/towards-data-science\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/towardsdatascience.com\/#\/schema\/person\/f9925d336b6fe962b03ad8281d90b8ee\",\"name\":\"TDS Editors\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/towardsdatascience.com\/#\/schema\/person\/image\/23494c9101089ad44ae88ce9d2f56aac\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g\",\"caption\":\"TDS Editors\"},\"description\":\"Building a vibrant data science and machine learning community. Share your insights and projects with our global audience: bit.ly\/write-for-tds\",\"url\":\"https:\/\/towardsdatascience.com\/author\/towardsdatascience\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/","og_locale":"en_US","og_type":"article","og_title":"Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science","og_description":"How StaticFrame and Python 3.13t enable true thread-based concurrency","og_url":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/","og_site_name":"Towards Data Science","article_published_time":"2025-07-07T17:05:53+00:00","article_modified_time":"2025-07-07T17:06:08+00:00","og_image":[{"width":1200,"height":675,"url":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/default-image.jpg","type":"image\/jpeg"}],"author":"Christopher Ariza","twitter_card":"summary_large_image","twitter_creator":"@TDataScience","twitter_site":"@TDataScience","twitter_misc":{"Written by":"Christopher Ariza","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#article","isPartOf":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/"},"author":{"name":"TDS Editors","@id":"https:\/\/towardsdatascience.com\/#\/schema\/person\/f9925d336b6fe962b03ad8281d90b8ee"},"headline":"Liberating Performance with Immutable DataFrames in Free-Threaded Python","datePublished":"2025-07-07T17:05:53+00:00","dateModified":"2025-07-07T17:06:08+00:00","mainEntityOfPage":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/"},"wordCount":1129,"publisher":{"@id":"https:\/\/towardsdatascience.com\/#organization"},"image":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage"},"thumbnailUrl":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg","keywords":["Dataframe","Immutability","Multithreading","Programming","Python"],"articleSection":["Data Science"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/","url":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/","name":"Liberating Performance with Immutable DataFrames in Free-Threaded Python | Towards Data Science","isPartOf":{"@id":"https:\/\/towardsdatascience.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage"},"image":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage"},"thumbnailUrl":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg","datePublished":"2025-07-07T17:05:53+00:00","dateModified":"2025-07-07T17:06:08+00:00","breadcrumb":{"@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#primaryimage","url":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg","contentUrl":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/07\/IMG_59631-scaled-1.jpg","width":2560,"height":1920,"caption":"Photo by Author."},{"@type":"BreadcrumbList","@id":"https:\/\/towardsdatascience.com\/liberating-performance-with-immutable-dataframes-in-free-threaded-python\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/towardsdatascience.com\/"},{"@type":"ListItem","position":2,"name":"Liberating Performance with Immutable DataFrames in Free-Threaded Python"}]},{"@type":"WebSite","@id":"https:\/\/towardsdatascience.com\/#website","url":"https:\/\/towardsdatascience.com\/","name":"Towards Data Science","description":"Publish AI, ML &amp; data-science insights to a global community of data professionals.","publisher":{"@id":"https:\/\/towardsdatascience.com\/#organization"},"alternateName":"TDS","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/towardsdatascience.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/towardsdatascience.com\/#organization","name":"Towards Data Science","alternateName":"TDS","url":"https:\/\/towardsdatascience.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/towardsdatascience.com\/#\/schema\/logo\/image\/","url":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/tds-logo.jpg","contentUrl":"https:\/\/towardsdatascience.com\/wp-content\/uploads\/2025\/02\/tds-logo.jpg","width":696,"height":696,"caption":"Towards Data Science"},"image":{"@id":"https:\/\/towardsdatascience.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/TDataScience","https:\/\/www.youtube.com\/c\/TowardsDataScience","https:\/\/www.linkedin.com\/company\/towards-data-science\/"]},{"@type":"Person","@id":"https:\/\/towardsdatascience.com\/#\/schema\/person\/f9925d336b6fe962b03ad8281d90b8ee","name":"TDS Editors","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/towardsdatascience.com\/#\/schema\/person\/image\/23494c9101089ad44ae88ce9d2f56aac","url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","caption":"TDS Editors"},"description":"Building a vibrant data science and machine learning community. Share your insights and projects with our global audience: bit.ly\/write-for-tds","url":"https:\/\/towardsdatascience.com\/author\/towardsdatascience\/"}]}},"distributor_meta":false,"distributor_terms":false,"distributor_media":false,"distributor_original_site_name":"TDS Contributor Portal","distributor_original_site_url":"https:\/\/contributor.insightmediagroup.io","push-errors":false,"_links":{"self":[{"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/posts\/606504","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/users\/18"}],"replies":[{"embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/comments?post=606504"}],"version-history":[{"count":0,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/posts\/606504\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/media\/606505"}],"wp:attachment":[{"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/media?parent=606504"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/categories?post=606504"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/tags?post=606504"},{"taxonomy":"sponsor","embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/sponsor?post=606504"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/towardsdatascience.com\/wp-json\/wp\/v2\/coauthors?post=606504"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}