pandas:对多列进行to_numeric操作

115

我正在使用以下数据框(df)

c.sort_values('2005', ascending=False).head(3)
      GeoName ComponentName     IndustryId IndustryClassification Description                                2004 2005  2006  2007  2008  2009 2010 2011 2012 2013 2014
37926 Alabama Real GDP by state 9          213                    Support activities for mining              99   98    117   117   115   87   96   95   103  102  (NA)
37951 Alabama Real GDP by state 34         42                     Wholesale trade                            9898 10613 10952 11034 11075 9722 9765 9703 9600 9884 10199
37932 Alabama Real GDP by state 15         327                    Nonmetallic mineral products manufacturing 980  968   940   1084  861   724  714  701  589  641  (NA)

我想要强制所有年份为数字格式:

c['2014'] = pd.to_numeric(c['2014'], errors='coerce')

有没有简单的方法来做这件事,还是我必须全部手动输入?

8个回答

152

更新:您无需在之后转换值,您可以在读取CSV文件时即时进行转换:

In [165]: df=pd.read_csv(url, index_col=0, na_values=['(NA)']).fillna(0)

In [166]: df.dtypes
Out[166]:
GeoName                    object
ComponentName              object
IndustryId                  int64
IndustryClassification     object
Description                object
2004                        int64
2005                        int64
2006                        int64
2007                        int64
2008                        int64
2009                        int64
2010                        int64
2011                        int64
2012                        int64
2013                        int64
2014                      float64
dtype: object

如果您需要将多个列转换为数字数据类型,请使用以下技术:

示例源数据框:

In [271]: df
Out[271]:
     id    a  b  c  d  e    f
0  id_3  AAA  6  3  5  8    1
1  id_9    3  7  5  7  3  BBB
2  id_7    4  2  3  5  4    2
3  id_0    7  3  5  7  9    4
4  id_0    2  4  6  4  0    2

In [272]: df.dtypes
Out[272]:
id    object
a     object
b      int64
c      int64
d      int64
e      int64
f     object
dtype: object

将选定的列转换为数字类型:

In [273]: cols = df.columns.drop('id')

In [274]: df[cols] = df[cols].apply(pd.to_numeric, errors='coerce')

In [275]: df
Out[275]:
     id    a  b  c  d  e    f
0  id_3  NaN  6  3  5  8  1.0
1  id_9  3.0  7  5  7  3  NaN
2  id_7  4.0  2  3  5  4  2.0
3  id_0  7.0  3  5  7  9  4.0
4  id_0  2.0  4  6  4  0  2.0

In [276]: df.dtypes
Out[276]:
id     object
a     float64
b       int64
c       int64
d       int64
e       int64
f     float64
dtype: object

如果您想选择所有的字符串 (object) 列,可以使用以下简单的技巧:

cols = df.columns[df.dtypes.eq('object')]

谢谢MaxU,感谢你的两个回答 :) - Collective Action
@MichaelPerdue,很高兴能帮忙 :) - MaxU - stand with Ukraine
为了完整起见:您还可以在初始化数据框时进行即时转换,例如:pd.DataFrame(datalist, dtype=float),这将尽可能将所有字段转换为浮点型(并将其他字段保持不变)。 - poppie
为什么当我这样做时,有些行变成NaN? - haneulkim
@h_musk,请在提供的解决方案中查看示例。所有无法转换为数字值的值都将变为NaN。 - MaxU - stand with Ukraine

100

另一种方法是使用apply,只需要一行代码:

cols = ['col1', 'col2', 'col3']
data[cols] = data[cols].apply(pd.to_numeric, errors='coerce', axis=1)

如果我们想将其转换为分类变量,而不是数值变量,该怎么办? - Scott85044
为什么axis=1呢?假设行数多于列数的情况下,按列应用会更快速。 - Rex
我建议移除axis=1,它会导致一个Traceback错误,错误信息为TypeError: to_numeric() got an unexpected keyword argument 'axis' - undefined

14

您可以使用:

print df.columns[5:]
Index([u'2004', u'2005', u'2006', u'2007', u'2008', u'2009', u'2010', u'2011',
       u'2012', u'2013', u'2014'],
      dtype='object')

for col in  df.columns[5:]:
    df[col] = pd.to_numeric(df[col], errors='coerce')

print df
       GeoName      ComponentName  IndustryId  IndustryClassification  \
37926  Alabama  Real GDP by state           9                     213   
37951  Alabama  Real GDP by state          34                      42   
37932  Alabama  Real GDP by state          15                     327   

                                      Description  2004   2005   2006   2007  \
37926               Support activities for mining    99     98    117    117   
37951                            Wholesale  trade  9898  10613  10952  11034   
37932  Nonmetallic mineral products manufacturing   980    968    940   1084   

        2008  2009  2010  2011  2012  2013     2014  
37926    115    87    96    95   103   102      NaN  
37951  11075  9722  9765  9703  9600  9884  10199.0  
37932    861   724   714   701   589   641      NaN  

使用 filter 的另一种解决方案:

print df.filter(like='20')
       2004   2005   2006   2007   2008  2009  2010  2011  2012  2013   2014
37926    99     98    117    117    115    87    96    95   103   102   (NA)
37951  9898  10613  10952  11034  11075  9722  9765  9703  9600  9884  10199
37932   980    968    940   1084    861   724   714   701   589   641   (NA)

for col in  df.filter(like='20').columns:
    df[col] = pd.to_numeric(df[col], errors='coerce')
print df
       GeoName      ComponentName  IndustryId  IndustryClassification  \
37926  Alabama  Real GDP by state           9                     213   
37951  Alabama  Real GDP by state          34                      42   
37932  Alabama  Real GDP by state          15                     327   

                                      Description  2004   2005   2006   2007  \
37926               Support activities for mining    99     98    117    117   
37951                            Wholesale  trade  9898  10613  10952  11034   
37932  Nonmetallic mineral products manufacturing   980    968    940   1084   

        2008  2009  2010  2011  2012  2013     2014  
37926    115    87    96    95   103   102      NaN  
37951  11075  9722  9765  9703  9600  9884  10199.0  
37932    861   724   714   701   589   641      NaN  

很高兴能帮助你!祝你好运! - jezrael

9
df[cols] = pd.to_numeric(df[cols].stack(), errors='coerce').unstack()

对我来说可以运行。将多个列转换为数字的更清晰的方法。 - Blessy
正如@Blessy所说,这种方法更加简洁和清晰,可以处理NaN值。 - Andres Mitre
在我的使用情况下,比apply稍微慢一些。 - Skippy le Grand Gourou

3

如果您正在寻找一系列的列,可以尝试这个方法:

df.iloc[7:] = df.iloc[7:].astype(float)

上述示例将把从第7列到末尾的所有列转换为浮点类型。当然,您可以使用不同的类型或范围。
如果您需要转换大量行和列,我认为这很有用。它可以让您避免手动处理每一行 - 我相信numpy可以更有效地完成此任务。
请注意,仅当您知道所有所需列仅包含数字时,此方法才有用 - 它无法将“错误值”(如字符串)更改为NaN。

对我来说,它并没有从第7列开始,而是仍然从第一列开始。 - Relative0
对我也是一样。它从第一列开始。 - Basilique
astype(float) 不能单独替换 pd.to_numeric,后者包含了 errors='coerce' 的参数,在这个问题中非常重要。否则会触发异常。 - mins

1
df.loc[:,'col':] = df.loc[:,'col':].apply(pd.to_numeric, errors = 'coerce')

3
尽管这段代码可能能够解决问题,但最好加入一些背景说明来解释它是如何解决问题的。这可以帮助未来的用户学习,最终将这些知识应用到自己的代码中。当解释代码时,你也很有可能获得用户的积极反馈/支持票。 - Amit Verma
1
我最终来到这里是因为我确实尝试了这个方法,但它在使用df.loc时并不起作用。 - M. Schlenker

1
考虑应用.astype()或.convert_dtype()方法:第一行指定了数据框data_df中与include=中指定的dtypes相匹配的列的列表。第二行将此列列表指定的数据框的“切片”转换为不同的dtype。第三行中的列名列表columns_mdy指定了要从对象(即只包含数字字符的字符串)转换为'int16'类型的数据框的“切片”。这些方法简单直观。
请参考以下参考资料。这些页面的侧边栏包含了其他任务的有用方法的链接。

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.astype.html https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.convert_dtypes.html https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.select_dtypes.html

float64_cols = list(data_df.select_dtypes(include='float64').columns)
data_df[float64_cols] = data_df[float64_cols].astype('float32')
data_df[columns_mdy] = data_df[columns_mdy].astype('int16')

0
我对不同的解决方案进行了一些基准测试。最快的解决方案不会强制将错误转化为NaN值。
import pandas as pd
import numpy as np
import timeit
from platform import python_version
print("Python version: ", python_version())
print("pandas version: ", pd.__version__)
print("numpy version: ", np.__version__)

np.random.seed(24)
N = 100000
clean = pd.DataFrame({'A':np.random.choice([1,2,3,4,5], size=N),
                      'B':np.random.choice([1,4,9,6,5,11,22,33,44,55,66,77,88,99,3.21], size=N),
                      'C':np.random.choice([1.1,2.2,3.3,4.4], size=N),
                      'D':np.random.choice([7,0,8], size=N)})

cols = clean.columns
for col in cols:
    clean[col] =clean[col].astype(str)

dirty = clean.copy()
newdf = clean.copy()
for col in cols:
    dirty.loc[dirty.sample(frac=0.05).index, col] = ""
    dirty.loc[dirty.sample(frac=0.03).index, col] = "spam"
    dirty.loc[dirty.sample(frac=0.03).index, col] = "eggs"


d1 = """
for col in cols:
    newdf[col] = pd.to_numeric(dirty[col], errors='coerce')
"""

d2 = """
newdf[cols] = dirty[cols].apply(pd.to_numeric, errors='coerce')
"""

d3 = """
newdf[cols] = dirty[cols].apply(pd.to_numeric, raw=True, errors='coerce')
"""

d4 = """
newdf[cols] = dirty[cols].transform(pd.to_numeric, errors='coerce')
"""

d5 = """
newdf[cols] = pd.to_numeric(dirty[cols].stack(), errors='coerce').unstack()
"""

c1 = """
newdf[cols] = clean[cols].astype(np.float64)
"""

c2 = """
newdf[cols] = clean[cols].values.astype(np.float64)
"""

num = 100
for i in [d1, d2, d3, d4, d5, c1, c2]:
    print(round(timeit.timeit(i, number=num, globals=globals()), 3))

这是输出结果:
Python version:  3.10.13
pandas version:  2.0.3
numpy version:  1.25.2
22.585  # d1
23.992  # d2
24.029  # d3
23.914  # d4
34.124  # d5
8.072   # c1
9.846   # c2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接