У меня есть файл tsv, содержащий целые числа с разделителями тысяч. Я пытаюсь прочитать его, используя polars==1.6.0
, кодировка utf-16
.
from io import BytesIO
import polars as pl
data = BytesIO(
"""
Id\tA\tB
1\t537\t2,288
2\t325\t1,047
3\t98\t194
""".encode("utf-16")
)
df = pl.read_csv(data, encoding = "utf-16", separator = "\t")
print(df)
Я не могу понять, как заставить поляры обрабатывать столбец «B» как целое число, а не как строку, и я также не могу найти чистый способ приведения его к целому числу.
shape: (3, 3)
┌────────┬─────┬───────┐
│ Id ┆ A ┆ B │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str │
╞════════╪═════╪═══════╡
│ 1 ┆ 537 ┆ 2,288 │
│ 2 ┆ 325 ┆ 1,047 │
│ 3 ┆ 98 ┆ 194 │
└────────┴─────┴───────┘
приведение не удается, как и явная передача схемы. Я также пробовал использовать str.strip_chars
, и чтобы удалить запятую, вместо этого я использую str.replace_all
.
df = df.with_columns(
pl.col("B").str.strip_chars(",").alias("B_strip_chars"),
pl.col("B").str.replace_all("[^0-9]", "").alias("B_replace"),
)
print(df)
shape: (3, 5)
┌────────┬─────┬───────┬───────────────┬───────────┐
│ Id ┆ A ┆ B ┆ B_strip_chars ┆ B_replace │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str ┆ str ┆ str │
╞════════╪═════╪═══════╪═══════════════╪═══════════╡
│ 1 ┆ 537 ┆ 2,288 ┆ 2,288 ┆ 2288 │
│ 2 ┆ 325 ┆ 1,047 ┆ 1,047 ┆ 1047 │
│ 3 ┆ 98 ┆ 194 ┆ 194 ┆ 194 │
└────────┴─────┴───────┴───────────────┴───────────┘
Кроме того, чтобы это работало в целом, мне нужно убедиться, что read_csv
не пытается вывести типы для каких-либо столбцов, чтобы я мог преобразовать их все вручную (любой числовой столбец со значением> 999 будет содержать запятую)
также pl.read_csv(..., use_pyarrow=True)
работает
Я попробовал use_pyarrow
, и, похоже, это не сработало, по крайней мере, при чтении из реального файла — мой игрушечный пример может быть не на 100% таким же. Я предполагал, что замена не удастся, если их будет больше 1 "," (т. е. 1 000 000), но я не пробовал.
Чтобы разрешить использование нескольких разделителей ,
, используйте .str.replace_all:
df = df.with_columns(pl.col('B').str.replace_all(",", "").cast(pl.Int64))
что дает для примера данных:
shape: (3, 3)
┌─────┬─────┬──────┐
│ Id ┆ A ┆ B │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 │
╞═════╪═════╪══════╡
│ 1 ┆ 537 ┆ 2288 │
│ 2 ┆ 325 ┆ 1047 │
│ 3 ┆ 98 ┆ 194 │
└─────┴─────┴──────┘
Чтобы сделать этот подход более общим, вместо pl.col('B')
вы можете использовать pl.col(pl.String)
, чтобы он перехватывал любой столбец, который проходил в виде строки. Помимо этого вам может потребоваться обработка ошибок, но это зависит от данных.
Спасибо - я думаю, однако, чтобы сделать его полностью универсальным, мне нужно будет заставить все столбцы быть строковыми, когда я читаю CSV - в противном случае я "случайно" получу столбцы str или int в зависимости от того, содержат ли данные значение> 999 или нет
Если ваши исходные данные имеют формат utf-16 (или что-то еще, кроме utf-8), то Polars преобразует их в utf-8 через Python. Поскольку это в любом случае должно произойти, возможно, лучше выполнить это преобразование самостоятельно и заменить знаки «,» в середине, чтобы встроенная программа чтения csv поляров анализировала данные как числа в начале read_csv
, а не на последующем этапе.
data.seek(0)
pl.read_csv(data.read().decode('utf-16').replace(',','').encode('utf-8'), separator = "\t")
Просто чтобы подчеркнуть, что если ваши исходные данные уже имеют формат utf-8, то использование Python для replace
почти наверняка медленнее, чем ответ @user19077881. Делайте это только в том случае, если ваш источник не utf-8, потому что Polars все равно преобразует его в utf-8 в Python. Конечно, если у вас есть столбцы, которые на самом деле должны быть строками с запятыми, это не сработает, потому что они не знают разницы.
не уверен, что это лучший способ, но простой
.with_columns(pl.col.B.str.replace(",", "").cast(pl.Int32))
работает