Python 高效地将大数据帧列从float转换为int

Python 高效地将大数据帧列从float转换为int,python,pandas,dataframe,Python,Pandas,Dataframe,这不同于,因为我需要保持NaN值,所以我选择使用实验性的。这个问题的关键是试图避免循环 我们有许多大型医学数据集,我正在从SAS导入熊猫。大多数字段都是枚举类型,应该表示为整数,但它们以float64的形式出现,因为许多字段包含NaN值。熊猫实验中的积分阵列类型解决了NaN问题。但是,这些数据集非常大,我想根据数据本身在脚本中转换它们。下面的脚本可以工作,但速度非常慢,我已经找到了一种更具python风格或“Pandorable”风格的编写方法 # Convert any non-float f

这不同于,因为我需要保持NaN值,所以我选择使用实验性的。这个问题的关键是试图避免循环

我们有许多大型医学数据集,我正在从SAS导入熊猫。大多数字段都是枚举类型,应该表示为整数,但它们以float64的形式出现,因为许多字段包含NaN值。熊猫实验中的积分阵列类型解决了NaN问题。但是,这些数据集非常大,我想根据数据本身在脚本中转换它们。下面的脚本可以工作,但速度非常慢,我已经找到了一种更具python风格或“Pandorable”风格的编写方法

# Convert any non-float fields to IntegerArray (Int)
# Note than IntegerArrays are an experimental addition in Pandas 0.24. They
# allow integer columns to contain NaN fields like float columns.
#
# This is a rather brute-force technique that loops through every column
# and every row. There's got to be a more efficient way to do it since it 
# takes a long time and uses up a lot of memory.
def convert_integer (df):
    for col in df.columns:
        intcol_flag = True
        if df[col].dtype == 'float64':   # Assuming dtype is "float64"
            # TODO: Need to remove inner loop - SLOW!
            for val in df[col]:
                # If not NaN and the int() value is different from
                # the float value, then we have an actual float.
                if pd.notnull(val) and abs(val - int(val)) > 1e-6:
                    intcol_flag = False
                    break;
            # If not a float, change it to an Int based on size
            if intcol_flag:
                if df[col].abs().max() < 127:
                    df[col] = df[col].astype('Int8')
                elif df[col].abs().max() < 32767:
                    df[col] = df[col].astype('Int16')
                else:   # assuming no ints greater than 2147483647 
                    df[col] = df[col].astype('Int32') 
        print(f"{col} is {df[col].dtype}")
    return df
而且还是一样慢

以下是一些示例数据和所需的输出:

np.random.seed(10)
df = pd.DataFrame(np.random.choice([1, 2, 3.3, 5000, 111111, np.NaN], (3,9)), 
                  columns=[f'col{i}' for i in range(9)])
df

    col0    col1    col2    col3    col4    col5    col6    col7    col8
0   2.0     NaN   111111.0  1.0     2.0   5000.0  111111.0  2.0     NaN
1   1.0     NaN     2.0     3.3     1.0      2.0     1.0    3.3     1.0
2  111111.0 5000.0  1.0   111111.0  5000.0   1.0    5000.0  3.3     2.0

结果应该是:

col0 is Int32
col1 is Int16
col2 is Int32
col3 is float64
col4 is Int16
col5 is Int16
col6 is Int32
col7 is float64
col8 is Int8

找到需要对每种类型进行类型转换的列,然后对每种类型一次执行所有操作

样本数据 代码

找到需要对每种类型进行类型转换的列,然后对每种类型一次执行所有操作

样本数据 代码


可能重复的
apply
本质上是一个for循环,所以它对您没有好处。使用
s=df[col].notnull()&df[col].sub(df[col].astype(int)).abs().gt(1e6)
是否需要保留NaN值?这些是浮子型的。请参阅建议的重复QQ。您可以尝试对代码的不同部分计时,以了解需要花费很长时间的内容@DavidZemens是的,我想保留NAN,熊猫V0.24中添加的实验积分对这一点很有效。所以转换不是问题,只是效率(和丑陋的循环)。可能重复的
apply
本质上是一个for循环,所以它对您没有好处。使用
s=df[col].notnull()&df[col].sub(df[col].astype(int)).abs().gt(1e6)
是否需要保留NaN值?这些是浮子型的。请参阅建议的重复QQ。您可以尝试对代码的不同部分计时,以了解需要花费很长时间的内容@DavidZemens是的,我想保留NAN,熊猫V0.24中添加的实验积分对这一点很有效。所以转换不是问题,只是效率(和丑陋的循环)。我喜欢你的做法,但有一件事我没有说清楚,就是少数列实际上包含浮点值,而这些列无法转换。如果您不介意的话,我可以借用您的数据来解决这个问题。在我的问题中添加了数据,并修改了您的代码以包含浮点数据字段。@MikeBopf我更新了,这是一个小改动。我认为你在这个问题上的预期输出有点偏离。我修正了我的预期输出-糟糕的剪切和粘贴。我仍在尝试让您的解决方案与我的数据一起工作,因为我有一些特殊情况。但我认为它会起作用。我遇到了硬件问题,因此无法在我的完整数据集上测试它,但解决方案可以在测试数据上运行,我喜欢答案的巧妙性。我喜欢你的做法,但有一件事我并没有弄清楚,就是有一小部分列实际上包含浮点值,而这些列不能被转换。如果您不介意的话,我可以借用您的数据来解决这个问题。在我的问题中添加了数据,并修改了您的代码以包含浮点数据字段。@MikeBopf我更新了,这是一个小改动。我认为你在这个问题上的预期输出有点偏离。我修正了我的预期输出-糟糕的剪切和粘贴。我仍在尝试让您的解决方案与我的数据一起工作,因为我有一些特殊情况。不过,我认为它会起作用。我遇到了硬件问题,因此无法在完整的数据集上测试它,但解决方案可以在测试数据上运行,我喜欢答案的巧妙性。
col0 is Int32
col1 is Int16
col2 is Int32
col3 is float64
col4 is Int16
col5 is Int16
col6 is Int32
col7 is float64
col8 is Int8
import pandas as pd
import numpy as np

np.random.seed(10)
df = pd.DataFrame(np.random.choice([1, 2, 3.3, 5000, 111111, np.NaN], (3,9)), 
                  columns=[f'col{i}' for i in range(9)])
s = pd.cut(df.max(), bins=[0, 127, 32767, 2147483647], labels=['Int8', 'Int16', 'Int32'])
s = s.where((df.dtypes=='float') & (df.isnull() | (df%1 == 0)).all())
            # Cast previously       # If all values are 
            # float columns         # "I"nteger-like

for idx, gp in s.groupby(s):
    df.loc[:, gp.index] = df.loc[:, gp.index].astype(idx)
df.dtypes
#col0      Int32
#col1      Int16
#col2      Int32
#col3    float64
#col4      Int16
#col5      Int16
#col6      Int32
#col7    float64
#col8       Int8
#dtype: object

print(df)
#     col0  col1    col2      col3  col4  col5    col6  col7  col8
#0       2   NaN  111111       1.0     2  5000  111111   2.0   NaN
#1       1   NaN       2       3.3     1     2       1   3.3     1
#2  111111  5000       1  111111.0  5000     1    5000   3.3     2