【问题标题】:Dataframe: shifting values over columns数据框:在列上移动值
【发布时间】:2021-06-22 17:55:44
【问题描述】:

我的 s_x 列中有一个包含一些 NaN 值的数据框。如果其中存在NaN 值,我希望它们位于最后一列中。

示例:给定 [Nan, 1, Nan, 2]s_x 列中的值,我希望这些值在列上向左移动以产生 [1, 2, NaN, NaN]

示例 2:

我目前的解决方案很慢,因为我:

  • 遍历行
  • s_x 值转换为列表
  • 删除NaN
  • NaN 值向左填充列表
  • 将其写回数据帧

如何改进以下功能?值的顺序(从低到高)需要保持不变。每个值只能在一行的s_x 列中找到一次。

我知道通过解析到列表并返回来“离开熊猫逻辑”在性能方面存在问题,并且正在考虑尝试使用 lambda 函数来做到这一点,但没有得到任何结果。

我当前的代码作为一个最小的工作示例:

import pandas as pd
import numpy as np

def shift_values(df, leading_chars):
    """Shifts all values in columns with common leading chars to the left if there are NaN values.
    
    Example:   Given a row of [NaN, 1, NaN, 2]
    the values are shifted to [1, 2, NaN, NaN]
    
    """
    cols = [c for c in list(df.columns) if c[:len(leading_chars)] == leading_chars] 

    for index, row in df.iterrows():
        # create list without NaN values
        values = [v for v in row[cols] if not pd.isna(v)] 
        # pad with NaN to get correct number of values again
        values += [np.nan] * (len(cols) - len(values))  

        # overwrite row values with modified list
        for i, c in enumerate(cols): 
            row[c] = values[i]

        # overwrite row in the dataframe
        df.iloc[index] = row

    return df 

mylist = [["key", "s_1", "s_2", "s_3", "s_4"],
          [1, np.nan, 1, 2, np.nan],
          [1, 10, 20, 25, np.nan],
          [1, 10, np.nan, 25, np.nan]
         ]
df = pd.DataFrame(mylist[1:], columns=mylist[0])

print("______ PREVIOUS ______")
print(df.head())

df = shift_values(df, 's_')
print("______ RESULT ______")
print(df.head())

【问题讨论】:

    标签: python pandas dataframe numpy


    【解决方案1】:

    试试:

    df = df.transform(sorted, key=pd.isna, axis=1)
    print(df)
    

    打印:

       key   s_1   s_2   s_3  s_4
    0  1.0   1.0   2.0   NaN  NaN
    1  1.0  10.0  20.0  25.0  NaN
    2  1.0  10.0  25.0   NaN  NaN
    

    编辑:如果列不相邻:

    x = df.filter(regex=r"^s_")
    
    df.loc[:, x.columns] = df.loc[:, x.columns].transform(
        sorted, key=pd.isna, axis=1
    )
    print(df)
    

    【讨论】:

    • 如果我错了,请纠正我,但这将覆盖所有列,而不仅仅是我的 s_x 列?
    • @Cribber 如果你想对s_* 列进行排序,你可以使用df.filter(regex=r'^s_') 之类的东西——但这不是必需的——pythons sorted 是稳定排序的。
    • 您能否扩展您的解决方案,我将如何使用 df.filter?
    • @Cribber 试试df.loc[:, "s_1":"s_4"] = df.filter(regex=r"^s_").transform(sorted, key=pd.isna, axis=1)
    • @Cribber - 是的,它更慢,因为 apply 是在引擎盖下循环。
    【解决方案2】:

    为了提高性能,请仅对选定的列使用 justify

    #https://stackoverflow.com/a/44559180/2901002
    def justify(a, invalid_val=0, axis=1, side='left'):    
        """
        Justifies a 2D array
    
        Parameters
        ----------
        A : ndarray
            Input array to be justified
        axis : int
            Axis along which justification is to be made
        side : str
            Direction of justification. It could be 'left', 'right', 'up', 'down'
            It should be 'left' or 'right' for axis=1 and 'up' or 'down' for axis=0.
    
        """
    
        if invalid_val is np.nan:
            mask = ~np.isnan(a)
        else:
            mask = a!=invalid_val
        justified_mask = np.sort(mask,axis=axis)
        if (side=='up') | (side=='left'):
            justified_mask = np.flip(justified_mask,axis=axis)
        out = np.full(a.shape, invalid_val) 
        if axis==1:
            out[justified_mask] = a[mask]
        else:
            out.T[justified_mask.T] = a.T[mask.T]
        return out
    

    def shift_values(df, leading_chars):
        """Shifts all values in columns with common leading chars to the
           left if there are NaN values.
        
        Example:   Given a row of [NaN, 1, NaN, 2]
        the values are shifted to [1, 2, NaN, NaN]
        
        """
        cols = df.columns[df.columns.str.startswith(leading_chars)]
        df[cols] = justify(df[cols].to_numpy(),  invalid_val=np.nan, axis=1, side='left')
        return df
        
    
    df = shift_values(df, 's_')
    print("______ RESULT ______")
    print(df.head())
    ______ RESULT ______
       key   s_1   s_2   s_3  s_4
    0    1   1.0   2.0   NaN  NaN
    1    1  10.0  20.0  25.0  NaN
    2    1  10.0  25.0   NaN  NaN
    

    【讨论】:

      猜你喜欢
      • 2014-11-17
      • 1970-01-01
      • 2023-04-02
      • 2019-10-31
      • 1970-01-01
      • 1970-01-01
      • 2016-05-21
      • 1970-01-01
      • 2021-04-28
      相关资源
      最近更新 更多