【问题标题】:Pandas: Select values from specific columns of a DataFrame by rowPandas:按行从 DataFrame 的特定列中选择值
【发布时间】:2018-06-08 09:59:54
【问题描述】:

给定一个具有多列的DataFrame,我们如何逐行从特定列中选择值来创建一个新的Series?

df = pd.DataFrame({"A":[1,2,3,4], 
                   "B":[10,20,30,40], 
                   "C":[100,200,300,400]})
columns_to_select = ["B", "A", "A", "C"]

目标: [10, 2, 3, 400]

一种有效的方法是使用 apply 语句。

df["cols"] = columns_to_select
df.apply(lambda x: x[x.cols], axis=1)

不幸的是,这不是矢量化操作,并且在大型数据集上需要很长时间。任何想法将不胜感激。

【问题讨论】:

    标签: python pandas numpy indexing


    【解决方案1】:

    Pandas approach:

    In [22]: df['new'] = df.lookup(df.index, columns_to_select)
    
    In [23]: df
    Out[23]:
       A   B    C  new
    0  1  10  100   10
    1  2  20  200    2
    2  3  30  300    3
    3  4  40  400  400
    

    【讨论】:

    • 落后一秒。 ;-)
    • @Wen,是的,我知道这种感觉——对不起:)
    • @MaxU 这正是我想要的。谢谢!
    • @JasonSanchez,很高兴我能帮上忙 :)
    【解决方案2】:

    NumPy 方式

    这是使用 advanced indexing 的矢量化 NumPy 方式 -

    # Extract array data
    In [10]: a = df.values
    
    # Get integer based column IDs
    In [11]: col_idx = np.searchsorted(df.columns, columns_to_select)
    
    # Use NumPy's advanced indexing to extract relevant elem per row
    In [12]: a[np.arange(len(col_idx)), col_idx]
    Out[12]: array([ 10,   2,   3, 400])
    

    如果df的列名没有排序,我们需要使用sorter参数和np.searchsorted。为这样的通用 df 提取 col_idx 的代码将是:

    # https://stackoverflow.com/a/38489403/ @Divakar
    def column_index(df, query_cols):
        cols = df.columns.values
        sidx = np.argsort(cols)
        return sidx[np.searchsorted(cols,query_cols,sorter=sidx)]
    

    所以,col_idx 会像这样获得 -

    col_idx = column_index(df, columns_to_select)
    

    进一步优化

    分析它显示瓶颈是使用 np.searchsorted 处理字符串,这是 NumPy 通常的弱点,即对字符串不太好。因此,为了克服这个问题并使用列名是单个字母的特殊情况,我们可以快速将它们转换为数字,然后将它们提供给 searchsorted 以加快处理速度。

    因此,对于列名是单个字母并已排序的情况,获取基于整数的列 ID 的优化版本将是 -

    def column_index_singlechar_sorted(df, query_cols):
        c0 = np.fromstring(''.join(df.columns), dtype=np.uint8)
        c1 = np.fromstring(''.join(query_cols), dtype=np.uint8)
        return np.searchsorted(c0, c1)
    

    这给了我们解决方案的修改版本,就像这样 -

    a = df.values
    col_idx = column_index_singlechar_sorted(df, columns_to_select)
    out = pd.Series(a[np.arange(len(col_idx)), col_idx])
    

    时间安排 -

    In [149]: # Setup df with 26 uppercase column letters and many rows
         ...: import string
         ...: df = pd.DataFrame(np.random.randint(0,9,(1000000,26)))
         ...: s = list(string.uppercase[:df.shape[1]])
         ...: df.columns = s
         ...: idx = np.random.randint(0,df.shape[1],len(df))
         ...: columns_to_select = np.take(s, idx).tolist()
    
    # With df.lookup from @MaxU's soln
    In [150]: %timeit pd.Series(df.lookup(df.index, columns_to_select))
    10 loops, best of 3: 76.7 ms per loop
    
    # With proposed one from this soln
    In [151]: %%timeit
         ...: a = df.values
         ...: col_idx = column_index_singlechar_sorted(df, columns_to_select)
         ...: out = pd.Series(a[np.arange(len(col_idx)), col_idx])
    10 loops, best of 3: 59 ms per loop
    

    鉴于 df.lookup 解决了一般情况,这可能是一个更好的选择,但本文中显示的其他可能的优化也很方便!

    【讨论】:

      猜你喜欢
      • 2018-09-04
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 2017-01-14
      • 2019-01-12
      • 2020-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多