猿问

有效地从 Pandas DataFrame 创建一系列元组

我正在使用apply()现有 DataFrame 的值构造一系列元组。我需要构造元组中值的特定顺序,并将NaN除一列之外的所有列替换为'{}'.


以下函数可以产生所需的结果,但执行速度相当慢:


def build_insert_tuples_series(row):

    # Here I attempt to handle ordering the final tuple

    # I must also replace NaN with "{}" for all but v2 column.

    vals = [row['v2']]

    row_sans_v2 = row.drop(labels=['v2'])

    row_sans_v2.fillna("{}", inplace=True)

    res = [val for val in row_sans_token]

    vals += res

    return tuple(vals)


def generate_insert_values_series(df):

    df['insert_vals'] = df.apply(lambda x: build_insert_tuples_series(x), axis=1)

    return df['insert_vals']

原始数据帧:


    id   v1    v2

0  1.0  foo  quux

1  2.0  bar   foo

2  NaN  NaN   baz

调用时产生的数据帧generate_insert_values_series(df):


最终元组的顺序逻辑是 (v2, ..all_other_columns..)


    id   v1    v2       insert_vals

0  1.0  foo  quux  (quux, 1.0, foo)

1  2.0  bar   foo   (foo, 2.0, bar)

2  NaN  NaN   baz     (baz, {}, {})

计时函数以生成结果数据帧:


%%timeit

generate_insert_values_series(df)

100 loops, best of 3: 2.69 ms per loop

我觉得可能有一种方法可以更有效地构建系列,但不确定如何使用矢量化或其他方法来优化操作。


30秒到达战场
浏览 273回答 3
3回答

温温酱

zip, get, mask, fillna, 和sorted一件物有所值df.assign(    insert_vals=    [*zip(*map(df.mask(df.isna(), {}).get, sorted(df, key=lambda x: x != 'v2')))])    id   v1    v2       insert_vals0  1.0  foo  quux  (quux, 1.0, foo)1  2.0  bar   foo   (foo, 2.0, bar)2  NaN  NaN   baz     (baz, {}, {})少一行字get = df.mask(df.isna(), {}).getkey = lambda x: x != 'v2'cols = sorted(df, key=key)df.assign(insert_vals=[*zip(*map(get, cols))])    id   v1    v2       insert_vals0  1.0  foo  quux  (quux, 1.0, foo)1  2.0  bar   foo   (foo, 2.0, bar)2  NaN  NaN   baz     (baz, {}, {})这应该适用于旧版 pythonget = df.mask(df.isna(), {}).getkey = lambda x: x != 'v2'cols = sorted(df, key=key)df.assign(insert_vals=zip(*map(get, cols)))

莫回无

你不应该要做到这一点,因为你的新系列将失去所有的矢量化功能。但是,如果必须,您可以apply通过使用pd.DataFrame.itertuples列表推导式或map. 唯一的复杂之处是重新排序列,您可以通过转换为list:df = pd.concat([df]*10000, ignore_index=True)col_lst = df.columns.tolist()cols = [col_lst.pop(col_lst.index('v2'))] + col_lst%timeit list(df[cols].itertuples(index=False))  # 31.3 ms per loop%timeit [tuple(x) for x in df[cols].values]     # 74 ms per loop%timeit list(map(tuple, df[cols].values))       # 73 ms per loop上面的基准测试是在 Python 3.6.0 上进行的,但您可能会发现这些比apply在 2.7 上更有效。请注意,list最终版本不需要转换,因为在 v2.7 中map返回 a list。如果绝对必要,您可以fillna通过一系列:s = pd.Series([{} for _ in range(len(df.index))], index=df.index)for col in df[cols]:    df[cols].fillna(s)

MYYA

首先,您可以使用numpy替换null值dictsimport pandas as pdimport numpy as nptemp = pd.DataFrame({'id':[1,2, None], 'v1':['foo', 'bar', None], 'v2':['quux', 'foo', 'bar']})def replace_na(col):     return np.where(temp[col].isnull(), '{}', temp[col])def generate_tuple(df):    df['id'], df['v1'] = replace_na('id'), replace_na('v1')    return df.apply(lambda x: tuple([x['v2'], x['id'], x['v1']]), axis=1)你的收获是%%timeittemp['insert_tuple'] = generate_tuple(temp)>>>> 1000 loops, best of 3 : 1ms per loop如果您将其更改generate_tuple return为类似def generate_tuple(df):    df['id'], df['v1'] = replace_na('id'), replace_na('v1')    return list(zip(df['v2'], df['id'], df['v1']))你的收益变成:%%timeittemp['insert_tuple'] = generate_tuple(temp)1000 loops, best of 3 : 674 µs per loop
随时随地看视频慕课网APP

相关分类

Python
我要回答