首页 > 解决方案 > Python 根据另一列的值(通过数字索引确定)为新列赋值

问题描述

我有一个大数据框(2,000,000+ 行乘 2,000+ 列),我想根据每行中包含的数据创建一些新列。一列是一个数字,我想用作索引器来选择其他列中的数据。该索引器会针对每一行进行更改,这意味着无法按名称选择列,因此我需要使用数字索引。在确定了我需要的列之后,我想将这些值输出到一组新的列(之后我将执行一些计算)。

以下是我尝试过的简化版本:


# import packages

import pandas as pd
import numpy as np


# create dataframe

n = 10000
a_bar = 9; a_sd = 2
b_bar = 1000; b_sd = 100

np.random.seed(12345)
df = pd.DataFrame(dict(month_index=np.random.normal(a_bar, a_sd, size=n),
                       month_1=np.random.normal(b_bar, b_sd, size=n),
                       month_2=np.random.normal(b_bar, b_sd, size=n),
                       month_3=np.random.normal(b_bar, b_sd, size=n),
                       month_4=np.random.normal(b_bar, b_sd, size=n),
                       month_5=np.random.normal(b_bar, b_sd, size=n),
                       month_6=np.random.normal(b_bar, b_sd, size=n),
                       month_7=np.random.normal(b_bar, b_sd, size=n),
                       month_8=np.random.normal(b_bar, b_sd, size=n),
                       month_9=np.random.normal(b_bar, b_sd, size=n),
                       month_10=np.random.normal(b_bar, b_sd, size=n),
                       month_11=np.random.normal(b_bar, b_sd, size=n),
                       month_12=np.random.normal(b_bar, b_sd, size=n)
                       ),
                  columns=['month_index', 'month_1', 'month_2', 'month_3',\
                           'month_4', 'month_5', 'month_6', 'month_7',\
                           'month_8', 'month_9', 'month_10', 'month_11', 'month_12'])

# round all column values 
   
df = df.round()

# restrict value of month index

conditions = [df.month_index < 7, df.month_index > 12, (df.month_index >= 7) & (df.month_index <= 12)]
values = [7, 12, df.month_index]

df["month_index"] = np.select(conditions, values)

# reduce size of dataframe

for column in df.columns:
    df[column] = pd.to_numeric(df[column], downcast='integer')

# select relevant data using function

def select_columns(df):
    
    i = 0
         
    while i < len(df):

        j = df.at[i, 'month_index']
        df.at[i, "temp_1"] = df.iat[i, j-5]
        df.at[i, "temp_2"] = df.iat[i, j-4]
        df.at[i, "temp_3"] = df.iat[i, j-3]
        df.at[i, "temp_4"] = df.iat[i, j-2]
        df.at[i, "temp_5"] = df.iat[i, j-1]
        df.at[i, "temp_6"] = df.iat[i, j]
    
        i += 1
    
    return df

df = select_columns(df)

我发现这是非常低效的,我宁愿使用 pandas .apply 方法,或者(如果可能的话) vectorisation。虽然我已经尝试过 numpy 的矢量化功能,但我知道这种方法只有在列可以作为 numpy 数组传递给函数时才可行。我的问题是:

  1. 解决我的问题的最佳方法是什么(考虑到我正在使用的行数/列数)?
  2. 对具有大量列的数据帧进行矢量化是否可行?

标签: pythonpandasdataframenumpyloops

解决方案


提高性能最简单的方法是在 python 数据结构中构建值,然后将列一次附加到数据帧,而不是不断地将值附加到数据帧。

data_structure = []
mapping = {
 'temp_1': 5,
 'temp_2': 4,
 'temp_3': 3,
 'temp_4': 2,
 'temp_5': 1,
 'temp_6': 0
}

for row in df.itertuples():
    d = {}
    for name, column_offset in mapping.items():
        idx = row.month_index - column_offset 
        column_name = df.columns[idx]
        d[name] = getattr(row, column_name)
    data_structure.append(d)

newdf = pd.DataFrame(data_structure)
combined = pd.concat([df, newdf], axis=1)

如果上述解决方案不够快,您应该能够使用多处理将工作拆分到多个内核。您可以尝试使用modin透明地执行此操作,或者您可能必须自己执行此操作。

至于矢量化,似乎 temp_1 在 month_index -5 列中。因此,以下面的简化示例为例,您应该能够使用矢量化来按列索引获取行值数组。

from io import StringIO
import numpy as np

# first five rows of dataframe for example
data = StringIO("""
9,1193,942,948,1053,922,985,949,987,970
10,884,1084,912,938,1179,958,842,944,1186
8,961,1070,1081,944,1074,988,1023,979,942
8,1040,1009,896,953,947,1058,1133,921,1113
12,913,1114,985,898,1011,1152,953,842,1150
""")

>>> a = np.genfromtxt(data, delimiter=',')
... temp_1_idx = (a[:, 0] - 5).astype(int)
... a[np.arange(len(a)), temp_1_idx]
array([1053., 1179., 1081.,  896.,  953.])

推荐阅读