NumPy หรือ Pandas: การรักษาประเภทอาร์เรย์ให้เป็นจำนวนเต็มในขณะที่มีค่า NaN


160

มีวิธีที่ต้องการเพื่อรักษาชนิดข้อมูลของnumpyอาร์เรย์คงที่เป็นint( int64หรืออะไรก็ตาม) ในขณะที่ยังคงมีองค์ประกอบภายในรายการเป็นnumpy.NaN?

โดยเฉพาะอย่างยิ่งฉันกำลังแปลงโครงสร้างข้อมูลภายในเป็น Pandas DataFrame ในโครงสร้างของเราเรามีคอลัมน์ประเภทจำนวนเต็มที่ยังคงมี NaN (แต่ dtype ของคอลัมน์นั้นเป็น int) มันดูเหมือนว่าจะแต่งทุกอย่างตามที่ลอยถ้าเราทำให้เรื่องนี้เป็น DataFrame intแต่เราจะชอบที่จะเป็น

คิด?

สิ่งที่พยายาม:

ฉันพยายามใช้from_records()ฟังก์ชั่นภายใต้ pandas.DataFrame ด้วยcoerce_float=Falseและสิ่งนี้ไม่ได้ช่วย ฉันยังลองใช้ NumPy masked arrays ด้วย NaN fill_value ซึ่งใช้งานไม่ได้ สิ่งเหล่านี้ทำให้ชนิดข้อมูลคอลัมน์กลายเป็นแบบลอย


คุณสามารถใช้อาเรย์สวมหน้ากากแบบ numpy ได้ไหม?
mgilson

ฉันจะลองดู ฉันยังพยายามfrom_recordsฟังก์ชั่นภายใต้ pandas.DataFrame ด้วยcoerce_float=Falseแต่โชคไม่ ... float64ก็ยังทำให้ข้อมูลใหม่มีประเภท
ely

1
ใช่ไม่มีโชค แม้จะมีอาร์เรย์ที่สวมหน้ากาก แต่ก็ยังแปลงเป็นแบบลอยได้ ดูเหมือนว่า Pandas จะเป็นเช่นนี้: "มี NaN อยู่ที่ใดหรือไม่ ... จากนั้นทุกอย่างก็จะลอย" หวังว่าจะมีวิธีแก้ไขปัญหานี้
ely

1
สนับสนุน Nullable Integer ซึ่งเป็นตัวเลือกเพิ่มเข้ามาอย่างเป็นทางการในนุ่น 0.24.0 - ในที่สุด :) - โปรดหาคำตอบที่ได้รับการปรับปรุง บันทึกย่อประจำรุ่น pandas 0.24.x
mork

คำตอบ:


70

ความสามารถนี้ถูกเพิ่มลงในแพนด้า (เริ่มต้นด้วยรุ่น 0.24): https://pandas.pydata.org/pandas-docs/version/0.24/whatsnew/v0.24.0.html#optional-integer-na-support

ณ จุดนี้มันต้องการการใช้งานส่วนขยาย dtype Int64 (พิมพ์ใหญ่) แทนที่จะเป็นค่าเริ่มต้น dtype int64 (ตัวพิมพ์เล็ก)


1
ตอนนี้คุณต้องระบุประเภทพิเศษที่ต้องการ'Int64'ให้มันใช้งานได้ จะดียิ่งขึ้นเมื่อเปิดใช้งานตามค่าเริ่มต้น
Jean Paul

มันเยี่ยมมาก! มีปัญหาเล็ก ๆ อยู่แม้ว่า PyCharm นั้นจะไม่สามารถแสดงดาต้าเฟรมในหน้าต่างดีบั๊กได้ถ้าใช้วิธีนี้ คุณสามารถดูคำตอบของฉันสำหรับคำถามอื่นเกี่ยวกับวิธีการบังคับให้แสดง: stackoverflow.com/questions/38956660/ … (ปัญหาดั้งเดิมมีแตกต่างกัน แต่วิธีแก้ปัญหาสำหรับการแสดงผลงาน dataframe)
Alaa M.

ฉันต้องใช้งาน'Int64'หรือมีบางอย่างที่เหมือนกัน'Int8'หรือไม่ np.floatจะใช้จำนวนเงินที่บ้าของหน่วยความจำเมื่อเทียบกับ
Superdooperhero

'Int8'ดูเหมือนว่าจะใช้งานได้ แต่np.floatก็ดูเหมือนว่าจะโหลดเร็วขึ้น ดูเหมือนว่าปัญหาจะไม่ปล่อยหน่วยความจำในระหว่าง สมมติว่าตัวรวบรวมขยะจะทำงานในที่สุด
Superdooperhero

103

NaNไม่สามารถเก็บไว้ในอาร์เรย์จำนวนเต็มได้ นี่เป็นข้อ จำกัด ที่ทราบกันดีของแพนด้าในขณะนี้ ฉันรอความคืบหน้าในการสร้างค่า NA ใน NumPy (คล้ายกับ NAs ใน R) แต่อย่างน้อย 6 เดือนถึงหนึ่งปีก่อนที่ NumPy จะได้รับคุณสมบัติเหล่านี้ดูเหมือนว่า:

http://pandas.pydata.org/pandas-docs/stable/gotchas.html#support-for-integer-na

(ฟีเจอร์นี้ได้รับการเพิ่มเริ่มต้นด้วยเวอร์ชัน 0.24 ของแพนด้า แต่โปรดทราบว่ามันต้องใช้ส่วนขยาย dtype Int64 (พิมพ์ใหญ่) แทนที่จะเป็น dtype int64 เริ่มต้น (ตัวพิมพ์เล็ก): https://pandas.pydata.org/pandas- docs / version / 0.24 / whatsnew / v0.24.0.html # optional-integer-na-support )


7
สวัสดีเวสต์มีการอัพเดตเกี่ยวกับสิ่งนี้หรือไม่? เราพบปัญหาที่คอลัมน์เข้าร่วมจะถูกแปลงเป็น ints หรือลอยตามการมีอยู่ของค่า NA ในรายการเดิม (การสร้างปัญหาในภายหลังเมื่อพยายามที่จะผสานดาต้าเบสเหล่านี้)
Carst


8

หากประสิทธิภาพไม่ใช่ปัญหาหลักคุณสามารถจัดเก็บสตริงได้

df.col = df.col.dropna().apply(lambda x: str(int(x)) )

จากนั้นคุณสามารถผสมกับNaNมากที่สุดเท่าที่คุณต้องการ ถ้าคุณอยากที่จะมีจำนวนเต็มขึ้นอยู่กับโปรแกรมของคุณคุณสามารถใช้-1หรือ0หรือ1234567890หรือบางค่าอื่น ๆ NaNโดยเฉพาะที่จะเป็นตัวแทน

คุณสามารถทำซ้ำคอลัมน์ชั่วคราว: หนึ่งคอลัมน์ตามที่คุณมี อีกอันหนึ่งเป็นการทดลองโดยมี ints หรือสตริง จากนั้นใส่assertsในสถานที่ที่เหมาะสมทุกการตรวจสอบว่าทั้งสองอยู่ในซิงค์ หลังจากการทดสอบมากพอคุณสามารถปล่อยลอยได้


5

นี่ไม่ใช่วิธีแก้ปัญหาสำหรับทุกกรณี แต่ของฉัน (พิกัดจีโนม) ฉันได้ใช้ 0 เป็น NaN

a3['MapInfo'] = a3['MapInfo'].fillna(0).astype(int)

อย่างน้อยก็อนุญาตให้ใช้ชนิดคอลัมน์ 'เนทีฟ' ที่เหมาะสมการดำเนินการเช่นการลบการเปรียบเทียบ ฯลฯ ทำงานตามที่คาดไว้


5

Pandas v0.24 +

ฟังก์ชั่นที่รองรับNaNในชุดเลขจำนวนเต็มจะมีให้ตั้งแต่ v0.24 ขึ้นไป มีข้อมูลเกี่ยวกับเรื่องนี้ใน v0.24 ว่า "มีอะไรใหม่" ส่วนและรายละเอียดมากขึ้นภายใต้Nullable จำนวนเต็มชนิดข้อมูล

Pandas v0.23 และก่อนหน้า

โดยทั่วไปแล้ววิธีที่ดีที่สุดคือการทำงานกับfloatซีรี่ส์หากเป็นไปได้แม้ในกรณีที่ซีรีย์ถูกส่งintไปที่floatเนื่องจากมีการรวมNaNค่าไว้ด้วย สิ่งนี้ทำให้การคำนวณ NumPy แบบ vectorised โดยที่มิฉะนั้นจะประมวลผลลูประดับ Python

เอกสารแนะนำ : "ความเป็นไปได้อย่างหนึ่งคือการใช้dtype=objectอาร์เรย์แทน" ตัวอย่างเช่น:

s = pd.Series([1, 2, 3, np.nan])

print(s.astype(object))

0      1
1      2
2      3
3    NaN
dtype: object

สำหรับเหตุผลด้านความงามเช่นส่งออกไปยังไฟล์สิ่งนี้อาจเป็นที่นิยมมากกว่า

Pandas v0.23 และรุ่นก่อนหน้า: พื้นหลัง

NaNfloatถือว่าเป็น เอกสารปัจจุบัน ( ณ v0.23)ระบุเหตุผลที่ว่าทำไมชุดจำนวนเต็มจะ upcasted เพื่อfloat:

ในกรณีที่ไม่มีการสนับสนุน NA ที่มีประสิทธิภาพสูงที่ถูกสร้างขึ้นใน NumPy จากพื้นดินขึ้นไปความเสียหายหลักคือความสามารถในการแสดง NAs ในอาร์เรย์จำนวนเต็ม

การแลกเปลี่ยนนี้ทำเพื่อเหตุผลด้านความจำและประสิทธิภาพเป็นอย่างมากและเพื่อให้ซีรี่ส์ที่ได้รับนั้นยังคงเป็น "ตัวเลข"

เอกสารยังมีกฎสำหรับการอัปโหลดเนื่องจากการNaNรวม:

Typeclass   Promotion dtype for storing NAs
floating    no change
object      no change
integer     cast to float64
boolean     cast to object


1

ต้องการเพิ่มในกรณีที่คุณพยายามแปลงเวกเตอร์ลอย (1.143) เป็นจำนวนเต็ม (1) ที่มี NA แปลงเป็น 'Int64' dtype ใหม่จะทำให้คุณมีข้อผิดพลาด ในการแก้ปัญหานี้คุณต้องปัดเศษตัวเลขแล้วจึงทำ ".astype ('Int64')"

s1 = pd.Series([1.434, 2.343, np.nan])
#without round() the next line returns an error 
s1.astype('Int64')
#cannot safely cast non-equivalent float64 to int64
##with round() it works
s1.round().astype('Int64')
0      1
1      2
2    NaN
dtype: Int64

กรณีการใช้งานของฉันคือฉันมีชุดข้อมูลลอยตัวที่ฉันต้องการปัดเศษเป็น int แต่เมื่อคุณทำ. รอบ () '* .0' ที่ท้ายจำนวนยังคงอยู่ดังนั้นคุณสามารถวาง 0 จากท้ายโดย แปลงเป็น int


0

หากมีช่องว่างในข้อมูลข้อความคอลัมน์ที่ตามปกติจะเป็นจำนวนเต็มจะถูกส่งไปยังการลอยเป็น float64 dtype เพราะ int64 dtype ไม่สามารถจัดการกับโมฆะ สิ่งนี้อาจทำให้เกิดสคีมาที่ไม่สอดคล้องกันถ้าคุณกำลังโหลดหลายไฟล์บางไฟล์ด้วยช่องว่าง (ซึ่งจะจบลงด้วยการเป็น float64 และอื่น ๆ โดยที่จะไม่จบลงด้วย int64

รหัสนี้จะพยายามแปลงคอลัมน์ประเภทตัวเลขใด ๆ ให้เป็น Int64 (ซึ่งตรงกันข้ามกับ int64) เนื่องจาก Int64 สามารถจัดการค่า Null ได้

import pandas as pd
import numpy as np

#show datatypes before transformation
mydf.dtypes

for c in mydf.select_dtypes(np.number).columns:
    try:
        mydf[c] = mydf[c].astype('Int64')
        print('casted {} as Int64'.format(c))
    except:
        print('could not cast {} to Int64'.format(c))

#show datatypes after transformation
mydf.dtypes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.