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 OverflowFor 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?
read_csv_batched() does not respect batch sizes
python - Read csv in chunks with polars efficiently (with limited available RAM) - Stack Overflow
python - polars.read_csv vs polars.read_csv_batched vs polars.scan_csv? - Stack Overflow
read_csv_batched for compressed files
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.
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
polars.scan_csv produces a query plan (called a LazyFrame). You can then build you query and on the end call collect to materialize a DataFrame.
This is the case for all scan_ methods. The benefit of this is that the Polars optimizer then can push down optimizations into the readers. It can apply filters in the readers and only selects columns it needs. This can save a lot of work.
polars.read_csv can be seen as polars.scan_csv().collect(). E.g. you simply read all the data and immediately produce a DataFrame. This means that you might do work that was not needed. The Polars optimizer is not able to do anything if you want a result immediately.
I don't agree with the other answer that polars.read_csv only should be used when data is large. It is just as well suitable for smaller data.
polars.read_csv_batchedis pretty equivalent topandas.read_csv(iterator=True).polars.scan_csvdoesn't do anything until you perform an operation on the dataframe likedask.dataframe.read_csv(lazy loading).
Scenarios:
I use
pandas.read_csvwhen my data is messy or complex in structure and the data is not too largeI use
polars.read_csvwhen my data file is very large (> 10GB).
This is an answer based solely on my (humble) opinion.