我想将表示为列表列表的表转换为 pandas DataFrame。作为一个极其简化的示例:
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)
将列转换为适当类型的最佳方法是什么,在这种情况下,第 2 列和第 3 列转换为浮点数?有没有办法在转换为 DataFrame 时指定类型?还是先创建 DataFrame 然后遍历列以更改每列的类型更好?理想情况下,我想以动态方式执行此操作,因为可能有数百列,我不想准确指定哪些列属于哪种类型。我只能保证每一列都包含相同类型的值。
在 pandas 中转换类型有四个主要选项:
to_numeric() - 提供安全地将非数字类型(例如字符串)转换为合适的数字类型的功能。 (另请参阅 to_datetime() 和 to_timedelta()。) astype() - 将(几乎)任何类型转换为(几乎)任何其他类型(即使这样做不一定明智)。还允许您转换为分类类型(非常有用)。 infer_objects() - 如果可能的话,一种实用方法,用于将保存 Python 对象的对象列转换为 pandas 类型。 convert_dtypes() - 将 DataFrame 列转换为支持 pd.NA 的“最佳”dtype(pandas 的对象表示缺失值)。
请继续阅读以了解每种方法的更详细说明和用法。
1. to_numeric()
将 DataFrame 的一列或多列转换为数值的最佳方法是使用 pandas.to_numeric()
。
此函数将尝试将非数字对象(如字符串)更改为适当的整数或浮点数。
基本用法
to_numeric()
的输入是一个系列或 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
如您所见,返回了一个新系列。请记住将此输出分配给变量或列名以继续使用它:
# convert Series
my_series = pd.to_numeric(my_series)
# convert column "a" of a DataFrame
df["a"] = pd.to_numeric(df["a"])
您还可以通过 apply()
方法使用它来转换 DataFrame 的多个列:
# 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)
只要您的值都可以转换,这可能就是您所需要的。
错误处理
但是如果某些值不能转换为数字类型怎么办?
to_numeric()
还接受一个 errors
关键字参数,允许您将非数字值强制为 NaN
,或者只是忽略包含这些值的列。
这是一个使用具有 object dtype 的一系列字符串 s
的示例:
>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0 1
1 2
2 4.7
3 pandas
4 10
dtype: object
如果无法转换值,默认行为是引发。在这种情况下,它无法处理字符串“pandas”:
>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string
我们可能希望“熊猫”被视为缺失/错误的数值,而不是失败。我们可以使用 errors
关键字参数将无效值强制为 NaN
,如下所示:
>>> pd.to_numeric(s, errors='coerce')
0 1.0
1 2.0
2 4.7
3 NaN
4 10.0
dtype: float64
errors
的第三个选项只是在遇到无效值时忽略该操作:
>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched
最后一个选项对于转换整个 DataFrame 特别有用,但不知道我们的哪些列可以可靠地转换为数字类型。在这种情况下,只需编写:
df.apply(pd.to_numeric, errors='ignore')
该函数将应用于 DataFrame 的每一列。可以转换为数字类型的列将被转换,而不能(例如,它们包含非数字字符串或日期)的列将被单独保留。
垂头丧气
默认情况下,使用 to_numeric()
的转换将为您提供 int64
或 float64
dtype(或您的平台原生的任何整数宽度)。
这通常是您想要的,但是如果您想节省一些内存并使用更紧凑的 dtype,例如 float32
或 int8
,该怎么办?
to_numeric()
让您可以选择向下转换为 'integer'
、'signed'
、'unsigned'
、'float'
。以下是整数类型的简单系列 s
的示例:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
向下转换为 'integer'
使用可以容纳这些值的最小可能整数:
>>> pd.to_numeric(s, downcast='integer')
0 1
1 2
2 -7
dtype: int8
向下转换为 'float'
类似地选择比正常浮动类型更小的:
>>> pd.to_numeric(s, downcast='float')
0 1.0
1 2.0
2 -7.0
dtype: float32
2. astype()
astype()
方法使您能够明确说明您希望 DataFrame 或 Series 具有的 dtype。它非常通用,您可以尝试从一种类型转换为任何其他类型。
基本用法
只需选择一种类型:您可以使用 NumPy dtype(例如 np.int16
)、一些 Python 类型(例如 bool)或 pandas 特定的类型(例如 categorical dtype)。
调用您要转换的对象的方法,astype()
将尝试为您转换它:
# 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')
请注意,我说的是“尝试” - 如果 astype()
不知道如何转换 Series 或 DataFrame 中的值,则会引发错误。例如,如果您有 NaN
或 inf
值,则在尝试将其转换为整数时会出错。
从 pandas 0.20.0 开始,可以通过传递 errors='ignore'
来抑制此错误。您的原始对象将原封不动地返回。
当心
astype()
功能强大,但有时会“错误地”转换值。例如:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
这些都是小整数,那么如何转换为无符号 8 位类型以节省内存?
>>> s.astype(np.uint8)
0 1
1 2
2 249
dtype: uint8
转换成功了,但是 -7 被环绕成 249(即 28 - 7)!
尝试改用 pd.to_numeric(s, downcast='unsigned')
向下转换可能有助于防止出现此错误。
3. infer_objects()
pandas 0.21.0 版引入了方法 infer_objects()
,用于将 DataFrame 中具有对象数据类型的列转换为更具体的类型(软转换)。
例如,这是一个包含两列对象类型的 DataFrame。一个保存实际整数,另一个保存表示整数的字符串:
>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a object
b object
dtype: object
使用 infer_objects()
,您可以将列“a”的类型更改为 int64:
>>> df = df.infer_objects()
>>> df.dtypes
a int64
b object
dtype: object
列 'b' 被单独留下,因为它的值是字符串,而不是整数。如果您想将两列强制为整数类型,则可以改用 df.astype(int)
。
4. 转换_dtypes()
1.0 及更高版本包括一个方法 convert_dtypes()
,用于将 Series 和 DataFrame 列转换为支持 pd.NA
缺失值的最佳数据类型。
这里“最好的”是指最适合保存这些值的类型。例如,这是一个 pandas 整数类型,如果所有值都是整数(或缺失值):Python 整数对象的对象列转换为 Int64
,NumPy int32
值的列将成为 pandas dtype Int32
。
使用我们的 object
DataFrame df
,我们得到以下结果:
>>> df.convert_dtypes().dtypes
a Int64
b string
dtype: object
由于列 'a' 保存整数值,它被转换为 Int64
类型(它能够保存缺失值,与 int64
不同)。
列“b”包含字符串对象,因此已更改为 pandas 的 string
dtype。
默认情况下,此方法将根据每列中的对象值推断类型。我们可以通过传递 infer_objects=False
来改变它:
>>> df.convert_dtypes(infer_objects=False).dtypes
a object
b string
dtype: object
现在列“a”仍然是一个对象列:pandas 知道它可以被描述为一个“整数”列(在内部它运行 infer_dtype
)但没有准确推断它应该具有什么整数 dtype,因此没有转换它。列 'b' 再次转换为 'string' dtype,因为它被识别为保存 'string' 值。
这个怎么样?
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
pd.DataFrame
有一个 dtype
参数,可以让您按照您的要求进行操作。 df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype=float) In [2]: df.dtypes Out[2]: 一个对象 两个 float64 三个 float64 dtype: object
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
。这可能是在较新版本的 pandas 中引入的,因此我没有发现任何问题,但我只是想知道这个警告是关于什么的。任何想法?
ValueError: Cannot convert NA to integer
df = pd.DataFrame(a, columns=['one', 'two', 'three'], dtype={'one': str, 'two': int, 'three': float})
。不过,我很难找到可接受的“dtype”值的规范。一个列表会很好(目前我做dict(enumerate(my_list))
)。
下面的代码将更改列的数据类型。
df[['col.name1', 'col.name2'...]] = df[['col.name1', 'col.name2'..]].astype('data_type')
代替数据类型,您可以提供数据类型。您想要什么,例如 str、float、int 等。
bool
将其应用于包含字符串 ``` 'True' ``` 和 ``` 'False' ``` 的列时,所有内容都会更改为 True
。
当我只需要指定特定列并且想要明确时,我使用过(每个 DOCS LOCATION):
dataframe = dataframe.astype({'col_name_1':'int','col_name_2':'float64', etc. ...})
因此,使用原始问题,但为其提供列名......
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'})
这是一个函数,它接受一个 DataFrame 和一个列列表作为其参数,并将列中的所有数据强制转换为数字。
# 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')
因此,对于您的示例:
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'])
熊猫 >= 1.0
这是一张图表,总结了 pandas 中一些最重要的转换。
https://i.stack.imgur.com/tUcdp.jpg
到字符串的转换是微不足道的 .astype(str)
并且未在图中显示。
“硬”与“软”转换
请注意,此上下文中的“转换”可以指将文本数据转换为其实际数据类型(硬转换),或者为对象列中的数据推断更合适的数据类型(软转换)。为了说明差异,请看一下
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
如何创建两个数据框,每个数据框的列具有不同的数据类型,然后将它们附加在一起?
d1 = pd.DataFrame(columns=[ 'float_column' ], dtype=float)
d1 = d1.append(pd.DataFrame(columns=[ 'string_column' ], dtype=str))
结果
In[8}: d1.dtypes
Out[8]:
float_column float64
string_column object
dtype: object
创建数据框后,您可以在第一列中使用浮点变量填充它,在第二列中使用字符串(或您想要的任何数据类型)填充它。
df = df.astype({"columnname": str})
#eg - 用于将列类型更改为字符串 #df 是您的数据框
type
提供一个示例。相反,赞成现有的答案。
df.info() 为我们提供了 temp 的初始数据类型,即 float64
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 132 non-null object
1 temp 132 non-null float64
现在,使用此代码将数据类型更改为 int64:
df['temp'] = df['temp'].astype('int64')
如果您再次执行 df.info(),您将看到:
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 132 non-null object
1 temp 132 non-null int64
这表明您已成功更改列 temp 的数据类型。快乐编码!
从 pandas 1.0.0 开始,我们有 pandas.DataFrame.convert_dtypes
。您甚至可以控制要转换的类型!
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
如果您有各种对象列,例如 74 个对象列和 2 个 Int 列的 Dataframe,其中每个值都有代表单位的字母:
import pandas as pd
import numpy as np
dataurl = 'https://raw.githubusercontent.com/RubenGavidia/Pandas_Portfolio.py/main/Wes_Mckinney.py/nutrition.csv'
nutrition = pd.read_csv(dataurl,index_col=[0])
nutrition.head(3)
name serving_size calories total_fat saturated_fat cholesterol sodium choline folate folic_acid ... fat saturated_fatty_acids monounsaturated_fatty_acids polyunsaturated_fatty_acids fatty_acids_total_trans alcohol ash caffeine theobromine water
0 Cornstarch 100 g 381 0.1g NaN 0 9.00 mg 0.4 mg 0.00 mcg 0.00 mcg ... 0.05 g 0.009 g 0.016 g 0.025 g 0.00 mg 0.0 g 0.09 g 0.00 mg 0.00 mg 8.32 g
1 Nuts, pecans 100 g 691 72g 6.2g 0 0.00 mg 40.5 mg 22.00 mcg 0.00 mcg ... 71.97 g 6.180 g 40.801 g 21.614 g 0.00 mg 0.0 g 1.49 g 0.00 mg 0.00 mg 3.52 g
2 Eggplant, raw 100 g 25 0.2g NaN 0 2.00 mg 6.9 mg 22.00 mcg 0.00 mcg ... 0.18 g 0.034 g 0.016 g 0.076 g 0.00 mg 0.0 g 0.66 g 0.00 mg 0.00 mg 92.30 g
3 rows × 76 columns
nutrition.dtypes
name object
serving_size object
calories int64
total_fat object
saturated_fat object
...
alcohol object
ash object
caffeine object
theobromine object
water object
Length: 76, dtype: object
nutrition.dtypes.value_counts()
object 74
int64 2
dtype: int64
将所有列转换为数字的一种好方法是使用正则表达式替换单位为空,使用 astype(float) 将列数据类型更改为浮点:
nutrition.index = pd.RangeIndex(start = 0, stop = 8789, step= 1)
nutrition.set_index('name',inplace = True)
nutrition.replace('[a-zA-Z]','', regex= True, inplace=True)
nutrition=nutrition.astype(float)
nutrition.head(3)
serving_size calories total_fat saturated_fat cholesterol sodium choline folate folic_acid niacin ... fat saturated_fatty_acids monounsaturated_fatty_acids polyunsaturated_fatty_acids fatty_acids_total_trans alcohol ash caffeine theobromine water
name
Cornstarch 100.0 381.0 0.1 NaN 0.0 9.0 0.4 0.0 0.0 0.000 ... 0.05 0.009 0.016 0.025 0.0 0.0 0.09 0.0 0.0 8.32
Nuts, pecans 100.0 691.0 72.0 6.2 0.0 0.0 40.5 22.0 0.0 1.167 ... 71.97 6.180 40.801 21.614 0.0 0.0 1.49 0.0 0.0 3.52
Eggplant, raw 100.0 25.0 0.2 NaN 0.0 2.0 6.9 22.0 0.0 0.649 ... 0.18 0.034 0.016 0.076 0.0 0.0 0.66 0.0 0.0 92.30
3 rows × 75 columns
nutrition.dtypes
serving_size float64
calories float64
total_fat float64
saturated_fat float64
cholesterol float64
...
alcohol float64
ash float64
caffeine float64
theobromine float64
water float64
Length: 75, dtype: object
nutrition.dtypes.value_counts()
float64 75
dtype: int64
现在数据集是干净的,您只能使用 regex 和 astype() 对此 Dataframe 进行数字运算。
如果您想收集单位并粘贴到 cholesterol_mg
等标题上,您可以使用以下代码:
nutrition.index = pd.RangeIndex(start = 0, stop = 8789, step= 1)
nutrition.set_index('name',inplace = True)
nutrition.astype(str).replace('[^a-zA-Z]','', regex= True)
units = nutrition.astype(str).replace('[^a-zA-Z]','', regex= True)
units = units.mode()
units = units.replace('', np.nan).dropna(axis=1)
mapper = { k: k + "_" + units[k].at[0] for k in units}
nutrition.rename(columns=mapper, inplace=True)
nutrition.replace('[a-zA-Z]','', regex= True, inplace=True)
nutrition=nutrition.astype(float)
有没有办法在转换为 DataFrame 时指定类型?
是的。其他答案在创建 DataFrame 后转换 dtypes,但我们可以在创建时指定类型。根据输入格式使用 DataFrame.from_records
或 read_csv(dtype=...)
。
后者有时是 avoid memory errors with big data 所必需的。
1.DataFrame.from_records
从所需列类型的 structured array 创建 DataFrame:
x = [['foo', '1.2', '70'], ['bar', '4.2', '5']]
df = pd.DataFrame.from_records(np.array(
[tuple(row) for row in x], # pass a list-of-tuples (x can be a list-of-lists or 2D array)
'object, float, int' # define the column types
))
输出:
>>> df.dtypes
# f0 object
# f1 float64
# f2 int64
# dtype: object
2. read_csv(dtype=...)
如果您从文件中读取数据,请使用 read_csv
的 dtype
参数在加载时设置列类型。
例如,这里我们读取 30M 行,其中 rating
为 8 位整数,genre
为分类:
lines = '''
foo,biography,5
bar,crime,4
baz,fantasy,3
qux,history,2
quux,horror,1
'''
columns = ['name', 'genre', 'rating']
csv = io.StringIO(lines * 6_000_000) # 30M lines
df = pd.read_csv(csv, names=columns, dtype={'rating': 'int8', 'genre': 'category'})
在这种情况下,我们在加载时将内存使用量减半:
>>> df.info(memory_usage='deep')
# memory usage: 1.8 GB
>>> pd.read_csv(io.StringIO(lines * 6_000_000)).info(memory_usage='deep')
# memory usage: 3.7 GB
这是avoid memory errors with big data的一种方法。 在加载后更改数据类型并不总是可能的,因为我们可能没有足够的内存来首先加载默认类型的数据。
我以为我有同样的问题,但实际上我有一点不同,这使得问题更容易解决。对于其他查看此问题的人,值得检查输入列表的格式。在我的情况下,数字最初是浮动的,而不是问题中的字符串:
a = [['a', 1.2, 4.2], ['b', 70, 0.03], ['x', 5, 0]]
但是通过在创建数据框之前过多地处理列表,我会丢失类型,并且所有内容都变成了字符串。
通过 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')
给出与问题相同的数据框,其中第 1 列和第 2 列中的条目被视为字符串。然而做
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')
实际上确实给出了一个数据框,其中的列格式正确
我遇到过同样的问题。我找不到任何令人满意的解决方案。我的解决方案只是将这些浮点数转换为 str 并以这种方式删除“.0”。
就我而言,我只是将其应用于第一列
firstCol = list(df.columns)[0]
df[firstCol] = df[firstCol].fillna('').astype(str).apply(lambda x: x.replace('.0', ''))
希望对某人有所帮助!
0.17
起已弃用.convert_objects
- 改用df.to_numeric
astype()
中error=coerce
?