【问题标题】:Pandas: efficient way to get a random subset from each row within a restricted column rangePandas:从受限列范围内的每一行中获取随机子集的有效方法
【发布时间】:2019-05-14 10:11:44
【问题描述】:

我有一些不同长度的数字时间序列存储在一个宽大的熊猫数据框中。每行对应一个系列,每列对应一个测量时间点。由于它们的长度不同,这些序列可能在左侧(第一个时间点)或右侧(最后一个时间点)或两者都有缺失值 (NA) 尾部。每行总是有一个没有最小长度的 NA 的连续条带。

我需要从这些行中的每一行中获取一个固定长度的随机子集,而不包括任何 NA。理想情况下,我希望保持原始数据帧完整,并在新数据帧中报告子集。

我设法通过一个效率非常低的 for 循环获得此输出,该循环逐行遍历每一行,确定裁剪位置的开始,以便 NA 不会包含在输出中并复制裁剪结果。这可行,但在大型数据集上非常慢。代码如下:

import pandas as pd
import numpy as np
from copy import copy

def crop_random(df_in, output_length, ignore_na_tails=True):
    # Initialize new dataframe
    colnames = ['X_' + str(i) for i in range(output_length)]
    df_crop = pd.DataFrame(index=df_in.index, columns=colnames)
    # Go through all rows
    for irow in range(df_in.shape[0]):
        series = copy(df_in.iloc[irow, :])
        series = np.array(series).astype('float')
        length = len(series)
        if ignore_na_tails:
            pos_non_na = np.where(~np.isnan(series))
            # Range where the subset might start
            lo = pos_non_na[0][0]
            hi = pos_non_na[0][-1]
            left = np.random.randint(lo, hi - output_length + 2)  
        else:
            left = np.random.randint(0, length - output_length)
        series = series[left : left + output_length]
        df_crop.iloc[irow, :] = series
    return df_crop

还有一个玩具例子:

df = pd.DataFrame.from_dict({'t0': [np.NaN, 1, np.NaN],
                             't1': [np.NaN, 2, np.NaN],
                             't2': [np.NaN, 3, np.NaN],
                             't3': [1, 4, 1],
                             't4': [2, 5, 2],
                             't5': [3, 6, 3],
                             't6': [4, 7, np.NaN],
                             't7': [5, 8, np.NaN],
                             't8': [6, 9, np.NaN]})
#     t0   t1   t2  t3  t4  t5   t6   t7   t8
# 0  NaN  NaN  NaN   1   2   3    4    5    6
# 1    1    2    3   4   5   6    7    8    9
# 2  NaN  NaN  NaN   1   2   3  NaN  NaN  NaN

crop_random(df, 3)
# One possible output:
#    X_0  X_1  X_2
# 0    2    3    4
# 1    7    8    9
# 2    1    2    3

我怎样才能以适应大型数据框的方式获得相同的结果?

编辑:将我改进的解决方案移至答案部分。

【问题讨论】:

    标签: python pandas subset


    【解决方案1】:

    我设法大大加快了速度:

    def crop_random(dataset, output_length, ignore_na_tails=True):
        # Get a random range to crop for each row
        def get_range_crop(series, output_length, ignore_na_tails):
            series = np.array(series).astype('float')
            if ignore_na_tails:
                pos_non_na = np.where(~np.isnan(series))
                start = pos_non_na[0][0]
                end = pos_non_na[0][-1]
                left = np.random.randint(start,
                                         end - output_length + 2)  # +1 to include last in randint; +1 for slction span
            else:
                length = len(series)
                left = np.random.randint(0, length - output_length)
            right = left + output_length
            return left, right
    
        # Crop the rows to random range, reset_index to do concat without recreating new columns
        range_subset = dataset.apply(get_range_crop, args=(output_length,ignore_na_tails, ), axis = 1)
        new_rows = [dataset.iloc[irow, range_subset[irow][0]: range_subset[irow][1]]
                    for irow in range(dataset.shape[0])]
        for row in new_rows:
            row.reset_index(drop=True, inplace=True)
    
        # Concatenate all rows
        dataset_cropped = pd.concat(new_rows, axis=1).T
    
        return dataset_cropped
    

    【讨论】:

      猜你喜欢
      • 2013-06-13
      • 2013-11-25
      • 2015-11-21
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-24
      相关资源
      最近更新 更多