Chuyển đổi cột Pandas chứa NaN thành dtype `int`


175

Tôi đọc dữ liệu từ tệp .csv đến khung dữ liệu Pandas như bên dưới. Đối với một trong các cột, cụ thể id, tôi muốn chỉ định loại cột là int. Vấn đề là idchuỗi có giá trị thiếu / trống.

Khi tôi cố gắng chuyển idcột thành số nguyên trong khi đọc .csv, tôi nhận được:

df= pd.read_csv("data.csv", dtype={'id': int}) 
error: Integer column has NA values

Ngoài ra, tôi đã cố gắng chuyển đổi loại cột sau khi đọc như dưới đây, nhưng lần này tôi nhận được:

df= pd.read_csv("data.csv") 
df[['id']] = df[['id']].astype(int)
error: Cannot convert NA to integer

Làm thế nào tôi có thể giải quyết điều này?


3
Tôi nghĩ rằng các giá trị số nguyên không thể được chuyển đổi hoặc lưu trữ trong một chuỗi / khung dữ liệu nếu thiếu các giá trị / NaN. Điều này tôi nghĩ là để làm với khả năng tương thích numpy (tôi đoán ở đây), nếu bạn muốn thiếu khả năng tương thích giá trị thì tôi sẽ lưu trữ các giá trị dưới dạng float
EdChum

1
xem tại đây: pandas.pydata.org/pandas-docs/dev/... ; bạn phải có một dtype float khi bạn thiếu các giá trị (hoặc đối tượng dtype về mặt kỹ thuật nhưng điều đó không hiệu quả); mục tiêu của bạn khi sử dụng kiểu int là gì?
Jeff

6
Tôi tin rằng đây là vấn đề NumPy, không dành riêng cho Pandas. Thật xấu hổ vì có rất nhiều trường hợp khi có một kiểu int cho phép khả năng của các giá trị null hiệu quả hơn nhiều so với một cột nổi lớn.
ely

1
Tôi có một vấn đề với điều này quá. Tôi có nhiều datafram mà tôi muốn hợp nhất dựa trên biểu diễn chuỗi của một số cột "số nguyên". Tuy nhiên, khi một trong các cột số nguyên đó có np.nan, việc truyền chuỗi tạo ra ".0", sẽ loại bỏ sự hợp nhất. Chỉ làm cho mọi thứ phức tạp hơn một chút, sẽ tốt hơn nếu có công việc đơn giản.
dermen

1
@Rhubarb, Hỗ trợ số nguyên không thể tùy chọn hiện được thêm chính thức vào gấu trúc 0.24.0 - cuối cùng :) - vui lòng tìm câu trả lời dưới đây. gấu trúc 0,24.x ghi chú phát hành
mork

Câu trả lời:


169

Việc thiếu đại diện NaN trong các cột số nguyên là một chú gấu trúc "gotcha" .

Cách giải quyết thông thường là chỉ cần sử dụng phao.


13
Có cách giải quyết nào khác ngoài việc đối xử với họ như phao không?
NumenorForLife

3
@ jsc123 bạn có thể sử dụng dtype đối tượng. Điều này đi kèm với một cảnh báo sức khỏe nhỏ nhưng phần lớn hoạt động tốt.
Andy Hayden

1
Bạn có thể cung cấp một ví dụ về cách sử dụng dtype đối tượng? Tôi đã xem qua các tài liệu về gấu trúc và googling, và tôi đã đọc nó là phương pháp được đề xuất. Nhưng, tôi đã không tìm thấy một ví dụ về cách sử dụng dtype đối tượng.
MikeyE

28
Trong v0.24, bây giờ bạn có thể thực hiện df = df.astype(pd.Int32Dtype())(để chuyển đổi toàn bộ khung dữ liệu hoặc) df['col'] = df['col'].astype(pd.Int32Dtype()). Các loại số nguyên nullable được chấp nhận khác là pd.Int16Dtypepd.Int64Dtype. Chọn thuốc độc của bạn.
cs95

1
Đó là giá trị NaN nhưng kiểm tra isnan hoàn toàn không hoạt động :(
Winston

116

Trong phiên bản 0.24. + Gấu trúc đã đạt được khả năng giữ các số nguyên với các giá trị bị thiếu.

Kiểu dữ liệu số nguyên Nullable .

Gấu trúc có thể biểu diễn dữ liệu số nguyên với các giá trị có thể thiếu bằng cách sử dụng arrays.IntegerArray. Đây là một loại mở rộng được thực hiện trong gấu trúc. Nó không phải là dtype mặc định cho số nguyên và sẽ không được suy ra; bạn rõ ràng phải chuyển dtype vào array()hoặc Series:

arr = pd.array([1, 2, np.nan], dtype=pd.Int64Dtype())
pd.Series(arr)

0      1
1      2
2    NaN
dtype: Int64

Để chuyển đổi cột thành số nguyên nullable, sử dụng:

df['myCol'] = df['myCol'].astype('Int64')

4
Tôi thích câu trả lời này.
cs95

7
Lưu ý rằng dtype phải "Int64"và không "int64"(đầu tiên 'i' phải được viết hoa)
Viacheslav Z

2
df.myCol = df.myCol.astype('Int64')hoặcdf['myCol'] = df['myCol'].astype('Int64')
LoMaPh

43

Trường hợp sử dụng của tôi là trộn dữ liệu trước khi tải vào bảng DB:

df[col] = df[col].fillna(-1)
df[col] = df[col].astype(int)
df[col] = df[col].astype(str)
df[col] = df[col].replace('-1', np.nan)

Loại bỏ NaN, chuyển đổi thành int, chuyển đổi sang str và sau đó gắn lại NAN.

Nó không đẹp nhưng nó hoàn thành công việc!


1
Tôi đã kéo tóc ra để thử tải số sê-ri trong đó một số là null và phần còn lại là số float, điều này đã cứu tôi.
Chris Decker

1
OP muốn một cột số nguyên. Chuyển đổi nó thành chuỗi không đáp ứng điều kiện.
Rishab Gupta

1
Chỉ hoạt động nếu col không có -1. Nếu không, nó sẽ gây rối với dữ liệu
Sharvari Gc

sau đó làm thế nào để quay lại int .. ??
abdoulsn

5

Bây giờ có thể tạo một cột gấu trúc chứa NaN dưới dạng dtype int, vì hiện tại nó đã được thêm chính thức vào gấu trúc 0.24.0

Ghi chú phát hành pandas 0.24.x Trích dẫn: " Pandas đã đạt được khả năng giữ các số nguyên với các giá trị bị thiếu


4

Nếu bạn hoàn toàn muốn kết hợp số nguyên và NaN trong một cột, bạn có thể sử dụng kiểu dữ liệu 'đối tượng':

df['col'] = (
    df['col'].fillna(0)
    .astype(int)
    .astype(object)
    .where(df['col'].notnull())
)

Điều này sẽ thay thế NaN bằng một số nguyên (không quan trọng), chuyển đổi thành int, chuyển đổi thành đối tượng và cuối cùng là gắn lại NaNs.


3

Nếu bạn có thể sửa đổi dữ liệu được lưu trữ của mình, hãy sử dụng giá trị sentinel cho thiếu id. Một trường hợp sử dụng phổ biến, được suy ra bởi tên cột, idlà một số nguyên, lớn hơn 0, bạn có thể sử dụng 0làm giá trị sentinel để bạn có thể viết

if row['id']:
   regular_process(row)
else:
   special_process(row)

3

Bạn có thể sử dụng .dropna()nếu có thể thả các hàng có giá trị NaN.

df = df.dropna(subset=['id'])

Ngoài ra, sử dụng .fillna().astype()thay thế NaN bằng các giá trị và chuyển đổi chúng thành int.

Tôi gặp vấn đề này khi xử lý tệp CSV có số nguyên lớn, trong khi một số trong số đó bị thiếu (NaN). Sử dụng float làm kiểu không phải là một lựa chọn, vì tôi có thể mất độ chính xác.

Giải pháp của tôi là sử dụng str làm loại trung gian . Sau đó, bạn có thể chuyển đổi chuỗi thành int như bạn muốn sau này trong mã. Tôi đã thay thế NaN bằng 0, nhưng bạn có thể chọn bất kỳ giá trị nào.

df = pd.read_csv(filename, dtype={'id':str})
df["id"] = df["id"].fillna("0").astype(int)

Đối với hình minh họa, đây là một ví dụ về cách phao có thể làm mất độ chính xác:

s = "12345678901234567890"
f = float(s)
i = int(f)
i2 = int(s)
print (f, i, i2)

Và đầu ra là:

1.2345678901234567e+19 12345678901234567168 12345678901234567890

2

Hầu hết các giải pháp ở đây cho bạn biết cách sử dụng số nguyên giữ chỗ để thể hiện null. Cách tiếp cận đó không hữu ích nếu bạn không chắc chắn rằng số nguyên sẽ không hiển thị trong dữ liệu nguồn của bạn. Phương thức của tôi với định dạng nổi sẽ không có giá trị thập phân của chúng và chuyển đổi null thành Không có. Kết quả là một kiểu dữ liệu đối tượng sẽ trông giống như một trường số nguyên với các giá trị null khi được tải vào CSV.

keep_df[col] = keep_df[col].apply(lambda x: None if pandas.isnull(x) else '{0:.0f}'.format(pandas.to_numeric(x)))

1

Tôi gặp vấn đề này khi làm việc với pyspark. Vì đây là một giao diện python cho mã chạy trên jvm, nó yêu cầu loại an toàn và sử dụng float thay vì int không phải là một tùy chọn. Tôi đã giải quyết vấn đề bằng cách gói gấu trúc pd.read_csvvào một hàm sẽ điền vào các cột do người dùng xác định bằng các giá trị điền do người dùng xác định trước khi chuyển chúng sang loại được yêu cầu. Đây là những gì tôi đã kết thúc bằng cách sử dụng:

def custom_read_csv(file_path, custom_dtype = None, fill_values = None, **kwargs):
    if custom_dtype is None:
        return pd.read_csv(file_path, **kwargs)
    else:
        assert 'dtype' not in kwargs.keys()
        df = pd.read_csv(file_path, dtype = {}, **kwargs)
        for col, typ in custom_dtype.items():
            if fill_values is None or col not in fill_values.keys():
                fill_val = -1
            else:
                fill_val = fill_values[col]
            df[col] = df[col].fillna(fill_val).astype(typ)
    return df

1
import pandas as pd

df= pd.read_csv("data.csv")
df['id'] = pd.to_numeric(df['id'])

4
Có một lý do bạn thích công thức này hơn đề xuất trong câu trả lời được chấp nhận? Nếu vậy, sẽ rất hữu ích khi chỉnh sửa câu trả lời của bạn để cung cấp lời giải thích đó và đặc biệt là vì có mười câu trả lời bổ sung đang cạnh tranh để được chú ý.
Jeremy Caney

Mặc dù mã này có thể giải quyết vấn đề của OP, tốt nhất là bao gồm một lời giải thích về cách thức / lý do mã của bạn giải quyết nó. Theo cách này, khách truy cập trong tương lai có thể học hỏi từ bài đăng của bạn và áp dụng nó vào mã của riêng họ. SO không phải là một dịch vụ mã hóa, mà là một nguồn tài nguyên cho kiến ​​thức. Ngoài ra, chất lượng cao, câu trả lời đầy đủ có nhiều khả năng được nâng cao. Các tính năng này, cùng với yêu cầu tất cả các bài đăng đều khép kín, là một số điểm mạnh của SO như một nền tảng khác biệt với các diễn đàn. Bạn có thể editthêm thông tin bổ sung & / hoặc để bổ sung giải thích của bạn với tài liệu nguồn.
SherylHohman

0

Đầu tiên loại bỏ các hàng có chứa NaN. Sau đó thực hiện chuyển đổi Integer trên các hàng còn lại. Cuối cùng chèn các hàng bị loại bỏ một lần nữa. Hy vọng nó sẽ làm việc


-1

Giả sử DateColumn của bạn được định dạng 3312018.0 nên được chuyển đổi thành ngày 31/03/2018 dưới dạng chuỗi. Và, một số hồ sơ bị thiếu hoặc 0.

df['DateColumn'] = df['DateColumn'].astype(int)
df['DateColumn'] = df['DateColumn'].astype(str)
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.zfill(8))
df.loc[df['DateColumn'] == '00000000','DateColumn'] = '01011980'
df['DateColumn'] = pd.to_datetime(df['DateColumn'], format="%m%d%Y")
df['DateColumn'] = df['DateColumn'].apply(lambda x: x.strftime('%m/%d/%Y'))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.