Ngăn chặn các khung dữ liệu gấu trúc trong khi lập chỉ mục và chèn các hàng


16

Tôi đang làm việc với từng hàng khung dữ liệu gấu trúc, nhưng tôi vấp phải các vấn đề cưỡng chế trong khi lập chỉ mục và chèn các hàng. Gấu trúc dường như luôn muốn ép buộc từ một kiểu int / float hỗn hợp sang các kiểu float hoàn toàn và tôi không thể thấy bất kỳ điều khiển rõ ràng nào về hành vi này.

Ví dụ, đây là một khung dữ liệu đơn giản với anhư intbnhư float:

import pandas as pd
pd.__version__  # '0.25.2'

df = pd.DataFrame({'a': [1], 'b': [2.2]})
print(df)
#    a    b
# 0  1  2.2
print(df.dtypes)
# a      int64
# b    float64
# dtype: object

Đây là một vấn đề cưỡng chế trong khi lập chỉ mục một hàng:

print(df.loc[0])
# a    1.0
# b    2.2
# Name: 0, dtype: float64
print(dict(df.loc[0]))
# {'a': 1.0, 'b': 2.2}

Và đây là một vấn đề cưỡng chế trong khi chèn một hàng:

df.loc[1] = {'a': 5, 'b': 4.4}
print(df)
#      a    b
# 0  1.0  2.2
# 1  5.0  4.4
print(df.dtypes)
# a    float64
# b    float64
# dtype: object

Trong cả hai trường hợp, tôi muốn acột vẫn là một kiểu số nguyên, thay vì bị ép buộc thành kiểu float.


Tôi đã tìm thấy điều này , nhưng tôi không thể tìm thấy nếu vấn đề được giải quyết một cách hiệu quả. Trong lúc này tôi đoán bạn có thể làm:df.loc[[0], df.columns]
Dani Mesejo


Âm thanh như pd.DataFrame không hỗ trợ trộn loại khi khởi tạo? pandas.pydata.org/pandas-docs/urdy/reference/api/ nhận dtype param chỉ hỗ trợ một loại duy nhất. .read_[type]hỗ trợ nhiều loại dtypes mặc dù ...
Quentin

Câu trả lời:


4

Sau khi đào bới, đây là một số cách giải quyết xấu xí khủng khiếp. (Một câu trả lời tốt hơn sẽ được chấp nhận.)

Một điều khó hiểu được tìm thấy ở đây là các cột không phải là số ngăn chặn sự ép buộc, vì vậy đây là cách lập chỉ mục một hàng thành một dict:

dict(df.assign(_='').loc[0].drop('_', axis=0))
# {'a': 1, 'b': 2.2}

Và chèn một hàng có thể được thực hiện bằng cách tạo khung dữ liệu mới với một hàng:

df = df.append(pd.DataFrame({'a': 5, 'b': 4.4}, index=[1]))
print(df)
#    a    b
# 0  1  2.2
# 1  5  4.4

Cả hai thủ thuật này đều không được tối ưu hóa cho các khung dữ liệu lớn, vì vậy tôi sẽ đánh giá rất cao câu trả lời tốt hơn!


Bạn luôn có thể ép buộc thêm bài viết df['a'] = df.a.astype(mytype)... Mặc dù vậy nó vẫn bẩn và có thể không hiệu quả.
Quentin

.astype()là nguy hiểm cho float -> số nguyên; nó không có vấn đề thay đổi 1.1để 1, vì vậy bạn thực sự cần phải đảm bảo tất cả các giá trị của bạn là 'nguyên giống như' trước khi làm việc đó. Có lẽ tốt nhất để sử dụng pd.to_numericvớidowncast='integer'
ALollz 11/11/19

2

Căn nguyên của vấn đề là

  1. Việc lập chỉ mục của dataframe gấu trúc trả về một chuỗi gấu trúc

Chúng tôi có thể thấy điều đó:

type(df.loc[0])
# pandas.core.series.Series

Và một loạt chỉ có thể có một dtype, trong trường hợp của bạn là int64 hoặc float64.

Có hai cách giải quyết đến trong đầu tôi:

print(df.loc[[0]])
# this will return a dataframe instead of series
# so the result will be
#    a    b
# 0  1  2.2

# but the dictionary is hard to read
print(dict(df.loc[[0]]))
# {'a': 0    1
# Name: a, dtype: int64, 'b': 0    2.2
# Name: b, dtype: float64}

hoặc là

print(df.astype(object).loc[0])
# this will change the type of value to object first and then print
# so the result will be
# a      1
# b    2.2
# Name: 0, dtype: object

print(dict(df.astype(object).loc[0]))
# in this way the dictionary is as expected
# {'a': 1, 'b': 2.2}
  1. Khi bạn nối từ điển vào khung dữ liệu, nó sẽ chuyển đổi từ điển thành Sê-ri trước rồi nối thêm. (Vì vậy, cùng một vấn đề xảy ra một lần nữa)

https://github.com/pandas-dev/pandas/blob/master/pandas/core/frame.py#L6973

if isinstance(other, dict):
    other = Series(other)

Vì vậy, bước đi của bạn thực sự là một nền tảng vững chắc, nếu không chúng ta có thể:

df.append(pd.Series({'a': 5, 'b': 4.4}, dtype=object, name=1))
#    a    b
# 0  1  2.2
# 1  5  4.4

Ý tưởng tốt để sử dụng objectcác loại dữ liệu! Một cách khác là tạo một DataFrame đối tượng ngay từ đầu:df = pd.DataFrame({'a': [1], 'b': [2.2]}, dtype=object)
Mike T

2

Bất cứ khi nào bạn nhận được dữ liệu từ khung dữ liệu hoặc nối thêm dữ liệu vào khung dữ liệu và cần giữ nguyên kiểu dữ liệu, hãy tránh chuyển đổi sang các cấu trúc bên trong khác không biết về các loại dữ liệu cần thiết.

Khi bạn làm df.loc[0]nó chuyển đổi thành pd.Series,

>>> type(df.loc[0])
<class 'pandas.core.series.Series'>

Và bây giờ, Seriessẽ chỉ có một dtype. Do đó ép buộc intđể float.

Thay vào đó giữ cấu trúc như pd.DataFrame,

>>> type(df.loc[[0]])
<class 'pandas.core.frame.DataFrame'>

Chọn hàng cần thiết làm khung và sau đó chuyển đổi thành dict

>>> df.loc[[0]].to_dict(orient='records')
[{'a': 1, 'b': 2.2}]

Tương tự, để thêm một hàng mới, Sử dụng pd.DataFrame.appendchức năng gấu trúc ,

>>> df = df.append([{'a': 5, 'b': 4.4}]) # NOTE: To append as a row, use []
   a    b
0  1  2.2
0  5  4.4

Ở trên sẽ không gây ra chuyển đổi loại,

>>> df.dtypes
a      int64
b    float64
dtype: object

Wow đã phải đọc khối mã thứ hai đó ba lần để có được nó. Điều đó rất tinh tế. Điều này tốt hơn nhiều so với những gì tôi đã làm trong quá khứ ... lặp qua khung dữ liệu cuối cùng và gán lại các giá trị với loại dữ liệu chính xác (vâng, những gì tôi đã làm là một giải pháp khủng khiếp thực sự sẽ không mở rộng được.).
VanBantam

1
Oh. Vui mừng vì nó đã giúp 😊 @VanBantam
Vishnudev

1

Một cách tiếp cận khác với các thao tác dữ liệu nhẹ:

Giả sử bạn có một danh sách từ điển (hoặc dataframes)

lod=[{'a': [1], 'b': [2.2]}, {'a': [5], 'b': [4.4]}]

trong đó mỗi từ điển đại diện cho một hàng (lưu ý các danh sách trong từ điển thứ hai). Sau đó, bạn có thể tạo một khung dữ liệu dễ dàng thông qua:

pd.concat([pd.DataFrame(dct) for dct in lod])
   a    b
0  1  2.2
0  5  4.4

và bạn duy trì các loại cột. Xem concat

Vì vậy, nếu bạn có một khung dữ liệu và một danh sách các ký tự, bạn chỉ có thể sử dụng

pd.concat([df] + [pd.DataFrame(dct) for dct in lod])

0

Trong trường hợp đầu tiên, bạn có thể làm việc với kiểu dữ liệu số nguyên nullable . Lựa chọn Sê-ri không ép buộc floatvà các giá trị được đặt trong một objectthùng chứa. Từ điển sau đó được tạo đúng, với giá trị cơ bản được lưu trữ dưới dạng a np.int64.

df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')

d = dict(df.loc[0])
#{'a': 1, 'b': 2.2}

type(d['a'])
#numpy.int64

Với cú pháp của bạn, điều này gần như cũng hoạt động cho trường hợp thứ hai, nhưng điều này phát lên object, vì vậy không tuyệt vời:

df.loc[1] = {'a': 5, 'b': 4.4}
#   a    b
#0  1  2.2
#1  5  4.4

df.dtypes
#a     object
#b    float64
#dtype: object

Tuy nhiên, chúng ta có thể thực hiện một thay đổi nhỏ đối với cú pháp để thêm một hàng ở cuối (với Range Index) và bây giờ các loại được xử lý đúng.

df = pd.DataFrame({'a': [1], 'b': [2.2]})
df['a'] = df['a'].astype('Int64')

df.loc[df.shape[0], :] = [5, 4.4]
#   a    b
#0  1  2.2
#1  5  4.4

df.dtypes
#a      Int64
#b    float64
#dtype: object
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.