It's hard to answer this question as I don't know what your other df looks like and I don't know exactly what you're doing that crashes your computer but here are a few observations/suggestions.

arrow and, by extension, polars isn't optimized for strings so one of the worst things you could do is load a giant file with all the columns being loaded as strings.

From the scan_csv docs

infer_schema_length Maximum number of lines to read to infer schema. If set to 0, all columns will be read as pl.Utf8.

You are setting that to 0 which means you're loading a giant file as all strings. Don't don't do that. Either set it to something reasonable or manually set the dtypes parameter. Ideally, the latter. Additionally, if there are columns that are strings but have relatively few unique values then you can make them categoricals to save more memory. Also, settings your integers to the smallest variation will also save some memory. One issue with Categoricals is that it's trickier to join on those columns. To get around that try:

import polars as pl
with pl.StringCache():
    dtypes={
    'siren':pl.UInt32(),
    'nic':pl.UInt32(),
    'siret':pl.UInt64(),
    'dateFin':pl.Date(),
    'dateDebut':pl.Date(),
    'etatAdministratifEtablissement':pl.Categorical(),
    'changementEtatAdministratifEtablissement':pl.Boolean(),
    'enseigne1Etablissement':pl.Categorical(),
    'enseigne2Etablissement':pl.Categorical(),
    'enseigne3Etablissement':pl.Categorical(),
    'changementEnseigneEtablissement':pl.Boolean(),
    'denominationUsuelleEtablissement':pl.Categorical(),
    'changementDenominationUsuelleEtablissement':pl.Boolean(),
    'activitePrincipaleEtablissement':pl.Categorical(),
    'nomenclatureActivitePrincipaleEtablissement':pl.Categorical(),
    'changementActivitePrincipaleEtablissement':pl.Boolean(),
    'caractereEmployeurEtablissement':pl.Categorical(),
    'changementCaractereEmployeurEtablissement':pl.Boolean()
    }

    df=pl.scan_csv("StockEtablissementHistorique_utf8.csv", 
                dtypes=dtypes)
    other_df=pl.read_database(your_query, your_conn_str).with_columns(pl.col(string_cols_to_join_on).cast(pl.Categorical())).lazy()
    

As an aside, some quick stats...

From here if I do df.collect().estimated_size('gb') it comes back as 4.57.

In contrast if I do pl.read_csv(thefile).estimated_size('gb') then it's 10.5 so there are significant savings in changing the column types.

You might need to keep everything in that StringCache context until you collect, I'm not sure how the StringCache interacts with LazyFrames. Since it takes basically no time for me to run the scan_csv, it's clearly not establishing the Categorical indexing right away.

It might also behoove you to resave the csv as multiple parquets that you then lazily load as a dataset via pyarrow.

Answer from Dean MacGregor on Stack Overflow
🌐
Polars
docs.pola.rs › py-polars › html › reference › api › polars.read_csv_batched.html
polars.read_csv_batched — Polars documentation
Read a CSV file in batches. Upon creation of the BatchedCsvReader, Polars will gather statistics and determine the file chunks.
🌐
Reddit
reddit.com › r/dataengineering › polars' scan_csv vs read_csv_batched
r/dataengineering on Reddit: Polars' scan_csv vs read_csv_batched
April 10, 2024 -

For what I understand scan could be thought of as a stream, while read_csv_batched is, well, in batches...

For context I am working with several different datasets, that although related in topic, the authors have ALL decided to use different information schemas.

What I want to know is: if I only care to get a quick feel of the dataset, which would be better memory-wise? (Because kernel keeps restarting)

And second, related question: If I open several files in the same fashion, would the previous scans/batches die, get converted into full dataframes, or something else?

Discussions

read_csv_batched() does not respect batch sizes
Data read successfully and validated.") # works with polars==1.12.0 # fails with polars==1.13.0 and 1.14.0, number of batches = 321 · Traceback (most recent call last): File "C:\Users\mysers\docs\bug_report_polars_read_csv_batches_big.py", line 49, in len(batches) == expected_batches ... More on github.com
🌐 github.com
8
November 25, 2024
python - Read csv in chunks with polars efficiently (with limited available RAM) - Stack Overflow
I've also tried the pl.read_csv_batched but it seems also to take forever (maybe to compute those first statistics not described in the documentation). The only way I found to handle the file with polars completly is to handles slices from a LazyFrame and collect it. More on stackoverflow.com
🌐 stackoverflow.com
python - polars.read_csv vs polars.read_csv_batched vs polars.scan_csv? - Stack Overflow
What is the difference between polars.read_csv vs polars.read_csv_batched vs polars.scan_csv ? More on stackoverflow.com
🌐 stackoverflow.com
read_csv_batched for compressed files
I am facing the following problem when trying to read a large zip file into polars using batches. reader = pl.read_csv_batched('file.zip',skip_rows=1,has_header=False) batches = reader.next_batches(2) # do something with batches More on github.com
🌐 github.com
4
March 22, 2023
🌐
GitHub
github.com › pola-rs › polars › issues › 19978
read_csv_batched() does not respect batch sizes · Issue #19978 · pola-rs/polars
November 25, 2024 - This parameters should be respected when reading a csv file with next_batches(1) - at least more or less. In version 1.12.0, this worked quite well. Since version 1.13.0, this does not work anymore. In the example above, we get 321 instead of 2 batches for a bigger file, and 1 batch instead of 7 (or 10) batches. Use version 1.12, then the assert statements will work. This should also work after the bug fix. ... --------Version info--------- Polars: 1.14.0 Index type: UInt32 Platform: Windows-10-10.0.22621-SP0 Python: 3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64
Author   pola-rs
🌐
Polars
docs.pola.rs › api › python › dev › reference › api › polars.io.csv.batched_reader.BatchedCsvReader.next_batches.html
polars.io.csv.batched_reader.BatchedCsvReader.next_batches — Polars documentation
Read n batches from the reader. Parameters: n · Number of chunks to fetch. Returns: list of DataFrames · Examples · >>> reader = pl.read_csv_batched( ... "./pdsh/tables_scale_100/lineitem.tbl", ... separator="|", ... try_parse_dates=True, ...
🌐
RubyDoc
rubydoc.info › gems › polars-df › Polars › IO:read_csv_batched
RubyDoc.info: Method: Polars::IO#read_csv_batched – Documentation for polars-df (0.23.0) – RubyDoc.info
Read a CSV file in batches. Upon creation of the BatchedCsvReader, polars will gather statistics and determine the file chunks.
Top answer
1 of 3
4

It's hard to answer this question as I don't know what your other df looks like and I don't know exactly what you're doing that crashes your computer but here are a few observations/suggestions.

arrow and, by extension, polars isn't optimized for strings so one of the worst things you could do is load a giant file with all the columns being loaded as strings.

From the scan_csv docs

infer_schema_length Maximum number of lines to read to infer schema. If set to 0, all columns will be read as pl.Utf8.

You are setting that to 0 which means you're loading a giant file as all strings. Don't don't do that. Either set it to something reasonable or manually set the dtypes parameter. Ideally, the latter. Additionally, if there are columns that are strings but have relatively few unique values then you can make them categoricals to save more memory. Also, settings your integers to the smallest variation will also save some memory. One issue with Categoricals is that it's trickier to join on those columns. To get around that try:

import polars as pl
with pl.StringCache():
    dtypes={
    'siren':pl.UInt32(),
    'nic':pl.UInt32(),
    'siret':pl.UInt64(),
    'dateFin':pl.Date(),
    'dateDebut':pl.Date(),
    'etatAdministratifEtablissement':pl.Categorical(),
    'changementEtatAdministratifEtablissement':pl.Boolean(),
    'enseigne1Etablissement':pl.Categorical(),
    'enseigne2Etablissement':pl.Categorical(),
    'enseigne3Etablissement':pl.Categorical(),
    'changementEnseigneEtablissement':pl.Boolean(),
    'denominationUsuelleEtablissement':pl.Categorical(),
    'changementDenominationUsuelleEtablissement':pl.Boolean(),
    'activitePrincipaleEtablissement':pl.Categorical(),
    'nomenclatureActivitePrincipaleEtablissement':pl.Categorical(),
    'changementActivitePrincipaleEtablissement':pl.Boolean(),
    'caractereEmployeurEtablissement':pl.Categorical(),
    'changementCaractereEmployeurEtablissement':pl.Boolean()
    }

    df=pl.scan_csv("StockEtablissementHistorique_utf8.csv", 
                dtypes=dtypes)
    other_df=pl.read_database(your_query, your_conn_str).with_columns(pl.col(string_cols_to_join_on).cast(pl.Categorical())).lazy()
    

As an aside, some quick stats...

From here if I do df.collect().estimated_size('gb') it comes back as 4.57.

In contrast if I do pl.read_csv(thefile).estimated_size('gb') then it's 10.5 so there are significant savings in changing the column types.

You might need to keep everything in that StringCache context until you collect, I'm not sure how the StringCache interacts with LazyFrames. Since it takes basically no time for me to run the scan_csv, it's clearly not establishing the Categorical indexing right away.

It might also behoove you to resave the csv as multiple parquets that you then lazily load as a dataset via pyarrow.

2 of 3
3

Note that polars also has a pl.read_csv_batched which gives you an iterator over chunks.

Example from the docs:



reader = pl.read_csv_batched(
    "./tpch/tables_scale_100/lineitem.tbl", separator="|", try_parse_dates=True
)  

batches = reader.next_batches(5)  

for df in batches:  
    print(df)

See docs page: https://pola-rs.github.io/polars/py-polars/html/reference/api/polars.read_csv_batched.html#polars.read_csv_batched

Other than that, follow the tips from Deans answer: https://stackoverflow.com/a/76392237/6717054

🌐
GitHub
github.com › pola-rs › polars › issues › 7699
read_csv_batched for compressed files · Issue #7699 · pola-rs/polars
March 22, 2023 - with fsspec.open('file.zip',compression='zip',mode='rb') as of: batches = of.readline() while batches: i = 0 df = pl.DataFrame() while i < 1000 and batches: batches = of.readline() df = pl.concat([df,pl.read_csv(batches,has_header=False)]) i +=1 # do something with batches · however, this solution is very slow and does not support multithreading. It would be great, if polars read_csv_batched could also work with compressed files as read_csv does.
Author   pola-rs
Find elsewhere
🌐
GitHub
github.com › pola-rs › polars › issues › 13885
read_csv_batched should return an instance of an iterator · Issue #13885 · pola-rs/polars
January 21, 2024 - Description read_csv_batched returns an instance of BatchedCsvReader, which we then need to call next_batches method on. We call it like so: reader = pl.read_csv_batched("big_file.csv") while (batches := reader.next_batches(100)) is not ...
Author   pola-rs
🌐
GitHub
github.com › pola-rs › polars › issues › 16953
read_csv_batched not working when separator is included in the field · Issue #16953 · pola-rs/polars
June 14, 2024 - import polars as pl reader_error = pl.read_csv_batched("error.csv", separator=",", batch_size=1, quote_char="\"") batch = reader_error.next_batches(2) print(len(batch)) # Prints 1, wrong print(batch) reader_correct = pl.read_csv_batched("correct.csv", separator=",", batch_size=1, quote_char="\"") batch = reader_correct.next_batches(2) print(len(batch)) # Prints 2, correct print(batch)
Author   pola-rs
🌐
GitHub
github.com › pola-rs › polars › issues › 10650
read_csv_batched is slow when dtypes is provided · Issue #10650 · pola-rs/polars
August 21, 2023 - I have confirmed this bug exists on the latest version of Polars. reader = pl.read_csv_batched("fpath/filename.csv", dtypes= my_types, columns=my_columns) batches = reader.next_batches(10)
Author   pola-rs
🌐
Rho Signal
rhosignal.com › posts › polars-glob-csvs
To go big you must be lazy | Rho Signal
December 22, 2024 - I was consulting for a client recently who needs to process hundreds of Gb of CSV files. On their first pass with Polars they had read from their CSVs with a pattern like this (simplified) version. I show it here with a breakdown below: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Create a list to hold a LazyFrame for each CSV queries = [] # Glob the CSV files csv_files = glob.glob("data_files/*.csv") # Loop over the CSV files for csv_file in csv_files: # Add the LazyFrame for each CSV file to the list queries.append(pl.scan_csv(csv_file)) # Evaluate all the queries in the list queries = pl.collect_all(queries) # Concatenate the DataFrames into a single DataFrame polars_df = pl.concat(queries) # Select a subset of columns polars_df.select(["date","temperature","humdity"]) ...
🌐
Polars
docs.pola.rs › api › python › stable › reference › api › polars.read_csv.html
polars.read_csv — Polars documentation
For file-like objects, the stream position may not be updated accordingly after reading. ... Indicate if the first row of the dataset is a header or not. If set to False, column names will be autogenerated in the following format: column_x, with x being an enumeration over every column in the dataset, starting at 1. ... Columns to select. Accepts a list of column indices (starting at zero) or a list of column names. ... Rename columns right after parsing the CSV file.
🌐
GitHub
github.com › pola-rs › polars › issues › 9056
`read_csv_batched` skips columns and ignores `batch_size` if `dtypes` partially provided · Issue #9056 · pola-rs/polars
May 26, 2023 - it seems to ignore batch_size, and loads all rows in the first batch. read_csv_batched works as expected if all columns are provided to dtypes or None.
Author   pola-rs
Top answer
1 of 1
2

You've found a known bug

Here's a workaround class to use in place of pl.read_csv_batched until that's fixed.

from math import ceil
import polars as pl
class BatchedPolarsCSV:
    def __init__(self, source: str, batch_size: int, **kwargs):
        if "batch_size" in kwargs:
            del kwargs["batch_size"]
        self._reader = pl.read_csv_batched(source, **kwargs)
        self._batch_size = batch_size
        self._native_batch_size = None
        self._cached_rows = []
        self._exhausted = False
    
    def _batches_to_get(self, target_rows:int)->int:
        need_rows = target_rows - self._have_rows()
        if self._native_batch_size:
            return ceil(need_rows/self._native_batch_size)
        else:
            return 1
    def _have_rows(self):
        return sum([x.height for x in self._cached_rows])
    
    def next_batches(self, n: int) -> None | list[pl.DataFrame]:
        if self._exhausted:
            return None
        target_rows = n * self._batch_size
        while self._have_rows() < target_rows:
            chunk = self._reader.next_batches(self._batches_to_get(target_rows))
            if chunk is None:
                break
            self._native_batch_size = chunk[0].height
            self._cached_rows.extend(chunk)
        if len(self._cached_rows)==0:
            self._exhausted=True
            return None
        df = pl.concat(self._cached_rows)
        return_part = df.slice(0, n * self._batch_size)
        cache_part = df.slice(n * self._batch_size)
        if cache_part.height > 0:
            self._cached_rows = [cache_part]
        else:
            self._cached_rows = []
        return return_part.with_columns(
            (pl.int_range(pl.len()) / self._batch_size)
            .floor()
            .alias(PARTITION_COL_NAME := "__parition_by_col")
        ).partition_by(PARTITION_COL_NAME, include_key=False)
🌐
Stack Overflow
stackoverflow.com › questions › 78616907 › polars-issue-with-read-csv-batched-when-separator-is-included-in-the-field
python - Polars - Issue with read_csv_batched when separator is included in the field - Stack Overflow
reader_error = pl.read_csv_batched("./assets/error.csv", separator=",", batch_size=1, quote_char="\"") batch = reader_error.next_batches(2) print(len(batch)) # Prints 1, wrong reader_correct = pl.read_csv_batched("./assets/correct.csv", separator=",", batch_size=1, quote_char="\"") batch = reader_correct.next_batches(2) print(len(batch)) # Prints 2, correct · Python: 3.10 Polars: 0.20.31 ·
🌐
GitHub
github.com › pola-rs › polars › issues › 13655
Reading a CSV file when the separator parameter is set to a non-default value, it will always load the entirety of its contents into memory. · Issue #13655 · pola-rs/polars
January 12, 2024 - When attempting to read a large 18GB CSV file using streaming or batched reading methods, setting the separator parameter to a non-default value might lead to a memory explosion, despite this phenomenon not being reflected in the Windows process manager. Additionally, I speculate that bug #9266 may be related to this issue. To avoid loading the entire content into memory, you can utilize streaming or batched reading methods instead. Polars: 0.20.3 Index type: UInt32 Platform: Windows-10-10.0.18363-SP0 Python: 3.11.5 | packaged by Anaconda, Inc.
Author   pola-rs
🌐
Polars
docs.pola.rs › py-polars › html › reference › io.html
Input/output — Polars documentation
read_avro(source, *[, columns, n_rows]) · Read into a DataFrame from Apache Avro format