Ubah tipe data kolom di Panda

806

Saya ingin mengonversi tabel, direpresentasikan sebagai daftar daftar, menjadi a Pandas DataFrame. Sebagai contoh yang sangat sederhana:

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)

Apa cara terbaik untuk mengubah kolom ke tipe yang sesuai, dalam hal ini kolom 2 dan 3 menjadi pelampung? Apakah ada cara untuk menentukan tipe saat mengonversi ke DataFrame? Atau lebih baik untuk membuat DataFrame terlebih dahulu dan kemudian melalui kolom untuk mengubah jenis untuk setiap kolom? Idealnya saya ingin melakukan ini dengan cara yang dinamis karena mungkin ada ratusan kolom dan saya tidak ingin menentukan kolom mana yang jenisnya tepat. Yang bisa saya jamin adalah bahwa setiap kolom berisi nilai dari tipe yang sama.

Sosiopat
sumber
Saya telah melihat pendekatan untuk mengonversi setiap kolom dan pendekatan untuk mengonversi kolom yang dinamai secara spesifik, tetapi bagaimana dengan kolom tertentu yang memenuhi kondisi tertentu ketika Anda tidak dapat membuat daftar 100 kolom yang ingin Anda konversi sekaligus? Saya berpikir untuk contoh semua float64 -> float32 atau taktik penghematan memori lainnya.
demongolem
@ demongolem: Anda bisa melakukan sesuatu seperti men- df.apply(pd.to_numeric, downcast="integer", errors="ignore")downcast kolom integer ke tipe (integer) terkecil yang akan menampung nilai-nilai.
Alex Riley

Jawaban:

1193

Anda memiliki tiga opsi utama untuk mengonversi jenis dalam panda:

  1. to_numeric()- menyediakan fungsionalitas untuk secara aman mengkonversi tipe non-numerik (mis. string) ke tipe numerik yang sesuai. (Lihat juga to_datetime()dan to_timedelta().)

  2. astype()- Mengkonversi (hampir) jenis apa pun menjadi (hampir) jenis lain (bahkan jika itu tidak selalu masuk akal untuk melakukannya). Anda juga dapat mengonversi ke jenis kategororial (sangat berguna).

  3. infer_objects() - metode utilitas untuk mengonversi kolom objek yang menyimpan objek Python ke tipe panda jika memungkinkan.

Baca terus untuk penjelasan lebih lanjut dan penggunaan masing-masing metode ini.


1. to_numeric()

Cara terbaik untuk mengonversi satu atau beberapa kolom DataFrame ke nilai numerik adalah dengan menggunakannya pandas.to_numeric().

Fungsi ini akan mencoba mengubah objek non-numerik (seperti string) menjadi bilangan bulat atau angka floating point yang sesuai.

Penggunaan dasar

Input ke to_numeric()adalah Seri atau kolom tunggal dari DataFrame.

>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # mixed string and numeric values
>>> s
0      8
1      6
2    7.5
3      3
4    0.9
dtype: object

>>> pd.to_numeric(s) # convert everything to float values
0    8.0
1    6.0
2    7.5
3    3.0
4    0.9
dtype: float64

Seperti yang Anda lihat, Seri baru dikembalikan. Ingatlah untuk menetapkan output ini ke nama variabel atau kolom untuk terus menggunakannya:

# convert Series
my_series = pd.to_numeric(my_series)

# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])

Anda juga dapat menggunakannya untuk mengonversi beberapa kolom DataFrame melalui apply()metode:

# convert all columns of DataFrame
df = df.apply(pd.to_numeric) # convert all columns of DataFrame

# convert just columns "a" and "b"
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)

Selama semua nilai Anda dapat dikonversi, mungkin itu yang Anda butuhkan.

Menangani kesalahan

Tetapi bagaimana jika beberapa nilai tidak dapat dikonversi ke tipe numerik?

to_numeric()juga mengambil errorsargumen kata kunci yang memungkinkan Anda untuk memaksa nilai non-numerik NaN, atau mengabaikan kolom yang berisi nilai-nilai ini.

Berikut ini contoh menggunakan serangkaian string syang memiliki objek tipe:

>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0         1
1         2
2       4.7
3    pandas
4        10
dtype: object

Perilaku default adalah untuk meningkatkan jika tidak dapat mengonversi nilai. Dalam hal ini, itu tidak dapat mengatasi string 'panda':

>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string

Daripada gagal, kita mungkin ingin 'panda' dianggap sebagai nilai numerik yang hilang / buruk. Kami dapat memaksa nilai yang tidak valid ke NaNsebagai berikut menggunakan errorsargumen kata kunci:

>>> pd.to_numeric(s, errors='coerce')
0     1.0
1     2.0
2     4.7
3     NaN
4    10.0
dtype: float64

Opsi ketiga errorsadalah mengabaikan operasi jika ada nilai yang tidak valid:

>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched

Opsi terakhir ini sangat berguna ketika Anda ingin mengonversi seluruh DataFrame Anda, tetapi tidak tahu kolom mana dari kami yang dapat dikonversi secara andal ke tipe numerik. Kalau begitu tulis saja:

df.apply(pd.to_numeric, errors='ignore')

Fungsi akan diterapkan ke setiap kolom DataFrame. Kolom yang dapat dikonversi ke tipe numerik akan dikonversi, sedangkan kolom yang tidak bisa (misalnya mengandung string atau tanggal non-digit) akan dibiarkan sendiri.

Downcasting

Secara default, konversi dengan to_numeric()akan memberi Anda tipe int64atau float64dtype (atau lebar integer apa pun yang asli untuk platform Anda).

Itu biasanya yang Anda inginkan, tetapi bagaimana jika Anda ingin menghemat memori dan menggunakan tipe yang lebih ringkas, seperti float32, atau int8?

to_numeric()memberi Anda pilihan untuk downcast ke 'integer', 'ditandatangani', 'unsigned', 'float'. Berikut ini contoh untuk serangkaian sederhana stipe integer:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Downcasting ke 'integer' menggunakan integer terkecil yang bisa menampung nilai-nilai:

>>> pd.to_numeric(s, downcast='integer')
0    1
1    2
2   -7
dtype: int8

Downcasting ke 'float' juga memilih tipe mengambang yang lebih kecil dari normal:

>>> pd.to_numeric(s, downcast='float')
0    1.0
1    2.0
2   -7.0
dtype: float32

2. astype()

The astype()Metode memungkinkan Anda untuk menjadi eksplisit tentang dtype Anda ingin DataFrame atau Series untuk memiliki. Ini sangat fleksibel karena Anda dapat mencoba dan beralih dari satu jenis ke jenis lainnya.

Penggunaan dasar

Cukup pilih satu jenis: Anda dapat menggunakan dtype NumPy (mis. np.int16), Beberapa jenis Python (misalnya bool), atau tipe khusus panda (seperti dtype kategoris).

Panggil metode pada objek yang ingin Anda konversi dan astype()akan mencoba dan mengubahnya untuk Anda:

# convert all DataFrame columns to the int64 dtype
df = df.astype(int)

# convert column "a" to int64 dtype and "b" to complex type
df = df.astype({"a": int, "b": complex})

# convert Series to float16 type
s = s.astype(np.float16)

# convert Series to Python strings
s = s.astype(str)

# convert Series to categorical type - see docs for more details
s = s.astype('category')

Perhatikan saya katakan "coba" - jika astype()tidak tahu cara mengonversi nilai dalam Seri atau DataFrame, itu akan menimbulkan kesalahan. Misalnya jika Anda memiliki NaNatau infnilai Anda akan mendapatkan kesalahan saat mencoba mengubahnya menjadi integer.

Pada panda 0.20.0, kesalahan ini dapat ditekan dengan melewati errors='ignore'. Objek asli Anda akan kembali tidak tersentuh.

Hati-hati

astype()memang kuat, tetapi terkadang akan mengonversi nilai "salah". Sebagai contoh:

>>> s = pd.Series([1, 2, -7])
>>> s
0    1
1    2
2   -7
dtype: int64

Ini adalah bilangan bulat kecil, jadi bagaimana dengan mengkonversi ke tipe 8-bit yang tidak ditandatangani untuk menghemat memori?

>>> s.astype(np.uint8)
0      1
1      2
2    249
dtype: uint8

Konversi berhasil, tetapi -7 dibungkus menjadi 249 (yaitu 2 8 - 7)!

Mencoba downcast menggunakan pd.to_numeric(s, downcast='unsigned')sebagai gantinya dapat membantu mencegah kesalahan ini.


3. infer_objects()

Versi 0.21.0 panda memperkenalkan metode infer_objects()untuk mengkonversi kolom DataFrame yang memiliki tipe data objek ke tipe yang lebih spesifik (konversi lembut).

Misalnya, inilah DataFrame dengan dua kolom tipe objek. Satu memegang bilangan bulat aktual dan lainnya memegang string yang mewakili bilangan bulat:

>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a    object
b    object
dtype: object

Dengan menggunakan infer_objects(), Anda dapat mengubah jenis kolom 'a' menjadi int64:

>>> df = df.infer_objects()
>>> df.dtypes
a     int64
b    object
dtype: object

Kolom 'b' ditinggalkan sendiri karena nilainya adalah string, bukan bilangan bulat. Jika Anda ingin mencoba dan memaksa konversi kedua kolom ke tipe integer, Anda bisa menggunakannya df.astype(int).

Alex Riley
sumber
8
Juga, tidak seperti .astype (float), ini akan mengonversi string ke NaN alih-alih meningkatkan kesalahan
Rob
11
.convert_objectsdihentikan sejak 0.17- gunakan df.to_numericsebagai gantinya
Matti Lyra
4
Terima kasih - saya harus memperbarui jawaban ini. Mungkin perlu dicatat bahwa pd.to_numericdan metode pendampingnya hanya akan bekerja pada satu kolom pada satu waktu, tidak seperti convert_objects. Diskusi tentang fungsi penggantian di API tampaknya sedang berlangsung ; Saya harap metode yang berfungsi di seluruh DataFrame akan tetap ada karena sangat berguna.
Alex Riley
Apa cara terbaik Anda mengonversi semua kolom yang saat ini, katakanlah int64menjadi int32?
RoyalTS
4
@RoyalTS: mungkin yang terbaik untuk digunakan astype(seperti pada jawaban lainnya), yaitu .astype(numpy.int32).
Alex Riley
447

Bagaimana dengan ini?

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['one', 'two', 'three'])
df
Out[16]: 
  one  two three
0   a  1.2   4.2
1   b   70  0.03
2   x    5     0

df.dtypes
Out[17]: 
one      object
two      object
three    object

df[['two', 'three']] = df[['two', 'three']].astype(float)

df.dtypes
Out[19]: 
one       object
two      float64
three    float64
hernamesbarbara
sumber
10
Iya! pd.DataFramememiliki dtypeargumen yang mungkin membuat Anda melakukan apa yang Anda cari. df = pd.DataFrame (a, kolom = ['satu', 'dua', 'tiga'], dtype = float) Dalam [2]: df.dtypes Out [2]: satu objek dua float64 tiga float64 dtype: objek
hernamesbarbara
17
Ketika saya mencoba seperti yang disarankan, saya mendapat peringatan SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead. Ini mungkin telah diperkenalkan di versi panda yang lebih baru dan saya tidak melihat ada yang salah sebagai hasilnya, tapi saya hanya ingin tahu apa peringatan ini. Ada ide?
oranye
2
@atau peringatan adalah untuk memperingatkan pengguna tentang perilaku yang berpotensi membingungkan dengan operasi berantai, dan dengan panda mengembalikan salinan daripada mengedit kerangka data. lihat stackoverflow.com/questions/20625582/… dan terkait.
A.Wan
19
Itu metode yang baik, tetapi tidak berfungsi ketika ada NaN di kolom Tidak tahu mengapa NaN tidak bisa tetap NaN ketika casting melayang ke int:ValueError: Cannot convert NA to integer
Vitaly Isaev
7
@GillBates ya, di kamus. df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float}). Saya kesulitan menemukan spesifikasi untuk nilai "dtype" yang diterima. Daftar akan lebih baik (saat ini saya lakukan dict(enumerate(my_list))).
FichteFoll
39

kode di bawah ini akan mengubah tipe data kolom.

df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')

di tempat tipe data Anda dapat memberikan tipe data Anda. apa yang Anda inginkan seperti str, float, int dll.

Akash Nayak
sumber
Pikiran Anda ketika menerapkan ini pada kolom yang berisi string `` `'Benar'` `` dan `` `'Salah'` `` menggunakan data_type bool, semuanya berubah menjadi True.
H. Vabri
Opsi ini Anda juga dapat mengonversi untuk mengetik "kategori"
neves
17

Ketika saya hanya perlu menentukan kolom tertentu, dan saya ingin menjadi eksplisit, saya telah menggunakan (per DOCS LOCATION ):

dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})

Jadi, menggunakan pertanyaan asli, tetapi memberikan nama kolom untuk itu ...

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col_name_1', 'col_name_2', 'col_name_3'])
df = df.astype({'col_name_2':'float64', 'col_name_3':'float64'})
Thom Ives
sumber
15

Berikut adalah fungsi yang mengambil sebagai argumen DataFrame dan daftar kolom dan memaksa semua data dalam kolom ke angka.

# df is the DataFrame, and column_list is a list of columns as strings (e.g ["col1","col2","col3"])
# dependencies: pandas

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

Jadi, untuk contoh Anda:

import pandas as pd

def coerce_df_columns_to_numeric(df, column_list):
    df[column_list] = df[column_list].apply(pd.to_numeric, errors='coerce')

a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a, columns=['col1','col2','col3'])

coerce_df_columns_to_numeric(df, ['col2','col3'])
Harry Stevens
sumber
bagaimana jika Anda ingin menggunakan indeks kolom alih-alih nama kolom?
jvalenti
8

Bagaimana dengan membuat dua kerangka data, masing-masing dengan tipe data yang berbeda untuk kolomnya, dan kemudian menambahkannya bersama?

d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))

Hasil

In[8}:  d1.dtypes
Out[8]: 
float_column     float64
string_column     object
dtype: object

Setelah bingkai data dibuat, Anda dapat mengisinya dengan variabel titik mengambang di kolom 1, dan string (atau tipe data apa pun yang Anda inginkan) di kolom ke-2.

MikeyE
sumber
4

panda> = 1.0

Berikut adalah bagan yang merangkum beberapa konversi terpenting dalam panda.

masukkan deskripsi gambar di sini

Konversi ke string sepele .astype(str)dan tidak ditampilkan dalam gambar.

Konversi "Keras" versus "Lembut"

Perhatikan bahwa "konversi" dalam konteks ini dapat merujuk pada konversi data teks menjadi tipe data aktual (konversi keras), atau menyimpulkan tipe data yang lebih tepat untuk data dalam kolom objek (konversi lunak). Untuk mengilustrasikan perbedaannya, lihat

df = pd.DataFrame({'a': ['1', '2', '3'], 'b': [4, 5, 6]}, dtype=object)
df.dtypes                                                                  

a    object
b    object
dtype: object

# Actually converts string to numeric - hard conversion
df.apply(pd.to_numeric).dtypes                                             

a    int64
b    int64
dtype: object

# Infers better data types for object data - soft conversion
df.infer_objects().dtypes                                                  

a    object  # no change
b     int64
dtype: object

# Same as infer_objects, but converts to equivalent ExtensionType
df.convert_dtypes().dtypes                                                     
cs95
sumber
1

Saya pikir saya memiliki masalah yang sama tetapi sebenarnya saya memiliki sedikit perbedaan yang membuat masalah lebih mudah untuk diselesaikan. Bagi orang lain yang melihat pertanyaan ini, ada baiknya memeriksa format daftar input Anda. Dalam kasus saya angka awalnya mengapung bukan string seperti dalam pertanyaan:

a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]

tetapi dengan memproses daftar terlalu banyak sebelum membuat dataframe saya kehilangan tipe dan semuanya menjadi string.

Membuat bingkai data melalui array numpy

df = pd.DataFrame(np.array(a))

df
Out[5]: 
   0    1     2
0  a  1.2   4.2
1  b   70  0.03
2  x    5     0

df[1].dtype
Out[7]: dtype('O')

memberikan bingkai data yang sama seperti pada pertanyaan, di mana entri dalam kolom 1 dan 2 dianggap sebagai string. Namun melakukan

df = pd.DataFrame(a)

df
Out[10]: 
   0     1     2
0  a   1.2  4.20
1  b  70.0  0.03
2  x   5.0  0.00

df[1].dtype
Out[11]: dtype('float64')

benar-benar memberikan bingkai data dengan kolom dalam format yang benar

SarahD
sumber
0

Mulai panda 1.0.0, kami punya pandas.DataFrame.convert_dtypes. Anda bahkan dapat mengontrol tipe yang akan dikonversi!

In [40]: df = pd.DataFrame(
    ...:     {
    ...:         "a": pd.Series([1, 2, 3], dtype=np.dtype("int32")),
    ...:         "b": pd.Series(["x", "y", "z"], dtype=np.dtype("O")),
    ...:         "c": pd.Series([True, False, np.nan], dtype=np.dtype("O")),
    ...:         "d": pd.Series(["h", "i", np.nan], dtype=np.dtype("O")),
    ...:         "e": pd.Series([10, np.nan, 20], dtype=np.dtype("float")),
    ...:         "f": pd.Series([np.nan, 100.5, 200], dtype=np.dtype("float")),
    ...:     }
    ...: )

In [41]: dff = df.copy()

In [42]: df 
Out[42]: 
   a  b      c    d     e      f
0  1  x   True    h  10.0    NaN
1  2  y  False    i   NaN  100.5
2  3  z    NaN  NaN  20.0  200.0

In [43]: df.dtypes
Out[43]: 
a      int32
b     object
c     object
d     object
e    float64
f    float64
dtype: object

In [44]: df = df.convert_dtypes()

In [45]: df.dtypes
Out[45]: 
a      Int32
b     string
c    boolean
d     string
e      Int64
f    float64
dtype: object

In [46]: dff = dff.convert_dtypes(convert_boolean = False)

In [47]: dff.dtypes
Out[47]: 
a      Int32
b     string
c     object
d     string
e      Int64
f    float64
dtype: object
Sohail
sumber