我有一个包含多列的pandas数据框。我的目标是将一个复杂的函数应用于3列并获得一个新的值列。然而,我想将相同的函数应用于不同的三元组列。是否有可能使用智能字符串格式,因此我不必对列的不同名称进行硬编码5次(或更多次)?
粗略草图: 列(' A1'' A2'' A3'' B1'' B2'&#39 ; B3',...)
def function(row):
return row['A1']**2 + row['A2']**3 + row['A3']**4 ### String format here?
为B1,2,3做同样的事情; C1,2,3等。
谢谢!
答案 0 :(得分:1)
使用@ Milo的设置数据帧df
np.random.seed(42)
col_names = 'A1 A2 A3 B1 B2 B3 C1 C2 C3'.split()
df = pd.DataFrame(np.random.rand(5,9), columns=col_names)
print(df)
A1 A2 A3 B1 B2 B3 C1 C2 C3
0 0.37 0.95 0.73 0.60 0.16 0.16 0.06 0.87 0.60
1 0.71 0.02 0.97 0.83 0.21 0.18 0.18 0.30 0.52
2 0.43 0.29 0.61 0.14 0.29 0.37 0.46 0.79 0.20
3 0.51 0.59 0.05 0.61 0.17 0.07 0.95 0.97 0.81
4 0.30 0.10 0.68 0.44 0.12 0.50 0.03 0.91 0.26
然后使用groupby
列或axis=1
。我们使用列标题中的第一个字母作为分组键。
df.pow(2).groupby(df.columns.str[0], 1).sum(axis=1).pow(.5)
A B C
0 1.256962 0.638019 1.055923
1 1.201048 0.878128 0.633695
2 0.803589 0.488905 0.929715
3 0.785843 0.634367 1.576812
4 0.755317 0.673667 0.946051
答案 1 :(得分:0)
如果我正确理解您的问题,您希望根据特定方案命名您的列,例如" A number "然后对它们应用相同的操作。
您可以通过使用正则表达式来filter获取要解决的列的命名方案,然后使用apply方法应用您的函数。
让我们看一个例子。我将首先构建一个像这样的DataFrame:
import pandas as pd
import numpy as np
np.random.seed(42)
col_names = 'A1 A2 A3 B1 B2 B3 C1 C2 C3'.split()
df = pd.DataFrame(np.random.rand(5,9), columns=col_names)
print df
A1 A2 A3 B1 B2 B3 C1 \
0 0.374540 0.950714 0.731994 0.598658 0.156019 0.155995 0.058084
1 0.708073 0.020584 0.969910 0.832443 0.212339 0.181825 0.183405
2 0.431945 0.291229 0.611853 0.139494 0.292145 0.366362 0.456070
3 0.514234 0.592415 0.046450 0.607545 0.170524 0.065052 0.948886
4 0.304614 0.097672 0.684233 0.440152 0.122038 0.495177 0.034389
C2 C3
0 0.866176 0.601115
1 0.304242 0.524756
2 0.785176 0.199674
3 0.965632 0.808397
4 0.909320 0.258780
然后将filter
方法与正则表达式结合使用。我将使用lambda示例性地对每个值进行平方。但是你可以使用你喜欢的任何功能/操作:
print df.filter(regex=r'A\d+').apply(lambda x: x*x)
A1 A2 A3
0 0.140280 0.903858 0.535815
1 0.501367 0.000424 0.940725
2 0.186576 0.084814 0.374364
3 0.264437 0.350955 0.002158
4 0.092790 0.009540 0.468175
编辑(2017-07-10)
通过以上示例,您可以继续进行最终想要计算的内容。例如,我们可以计算所有A
- 列的欧氏距离,如下所示:
df.filter(regex=r'A\d+').apply(lambda x: x*x).sum(axis=1).apply(np.sqrt)
结果是:
0 1.256962
1 1.201048
2 0.803589
3 0.785843
4 0.755317
所以我们基本上计算的是每行的sqrt(A1 ^ 2 + A2 ^ 2 + A3 ^ 2 + ... + An ^ 2)。
但是,由于您希望将单独的转换应用于单独的列命名方案,因此您必须对上述方法串联进行硬编码。
更优雅的解决方案是使用pipelines。管道基本上允许您在DataFrame上定义操作,然后以您需要的方式组合它们。再次使用计算欧几里德距离的例子,我们可以构建如下的管道:
def filter_columns(dataframe, regex):
"""Filter out columns of `dataframe` matched by `regex`."""
return dataframe.filter(regex=regex)
def op_on_vals(dataframe, op_vals):
"""Apply `op_vals` to every value in the columns of `dataframe`"""
return dataframe.apply(op_vals)
def op_across_columns(dataframe, op_cols):
"""Apply `op_cols` across the columns of `dataframe`"""
# Catch exception that would be raised if function
# would be applied to a pandas.Series.
try:
return dataframe.apply(op_cols, axis=1)
except TypeError:
return dataframe.apply(op_cols)
对于每个列命名方案,您可以定义要应用的转换以及它们必须应用的顺序。例如,这可以通过创建一个字典来完成,该字典将列命名方案保存为键,将管道的参数保存为值:
pipe_dict = {r'A\d+': [(op_on_vals, np.square), (op_across_columns, np.sum), (op_across_columns, np.sqrt)],
r'B\d+': [(op_on_vals, np.square), (op_across_columns, np.mean)],
r'C\d+': [(op_on_vals, lambda x: x**3), (op_across_columns, np.max)]}
# First pipe: Euclidean distance
# Second pipe: Mean of squares
# Third pipe: Maximum cube
df_list = []
for scheme in pipe_dict.keys():
df_list.append(df.pipe(filter_columns, scheme))
for (operation, func) in pipe_dict[scheme]:
df_list[-1] = df_list[-1].pipe(operation, func)
print df_list[0]
0 1.256962
1 1.201048
2 0.803589
3 0.785843
4 0.755317
获得与上述相同的结果。
现在,这只是一个示例用途,既不优雅,也不高效。它只是为了演示DataFrame管道的概念。采用这些概念,您可以非常喜欢这个 - 例如定义管道管道等。
但是,以此示例为例,您可以实现定义要在列上执行的任意函数顺序的目标。您现在可以更进一步,一次将一个函数应用于特定列,而不是在所有列中应用函数。
例如,您可以使用我的op_on_vals
功能并对其进行修改,使其达到您使用row['A1']**2
,row['A2']**3
列出的内容,然后使用.pipe(op_across_columns, np.sum)
来实现您的功能草绘
def function(row):
return row['A1']**2 + row['A2']**3 + row['A3']**4
这不应该太难,所以我会把这个实现的细节留给你。
编辑(2017-07-11)
以下是使用functools.partial来创建'函数原型的另一段代码。功率函数。这些可用于根据DataFrame的列名中的数字可变地设置幂的指数。
这样我们就可以使用A1
,A2
等中的数字来计算相应列中每个值的value**1
,value**2
。最后,我们可以将它们相加以获得您使用
row['A1']**2 + row['A2']**3 + row['A3']**4
您可以找到functools.partial对PyDanny's Blog所做的一个很好的解释。我们来看看代码:
import pandas as pd
import numpy as np
import re
from functools import partial
def power(base, exponent):
return base ** exponent
# Create example DataFrame.
np.random.seed(42)
col_names = 'A1 A2 A3 B1 B2 B3 C1 C2 C3'.split()
df = pd.DataFrame(np.random.rand(5, 9), columns=col_names)
# Separate 'letter''number' strings of columns into tuples of (letter, number).
match = re.findall(r"([A-Z]+)([0-9]+)", ''.join(df.columns.tolist()))
# Dictionary with 'prototype' functions for each column naming scheme.
func_dict = {'A': power, 'B': power, 'C': power}
# Initialize result columns with zeros.
for letter, _ in match:
df[letter+'_result'] = np.zeros_like(df[letter+'1'])
# Apply functions to columns
for letter, number in match:
col_name = ''.join([letter, number])
teh_function = partial(func_dict[letter], exponent=int(number))
df[letter+'_result'] += df[col_name].apply(teh_function)
print df
输出:
A1 A2 A3 B1 B2 B3 C1 \
0 0.374540 0.950714 0.731994 0.598658 0.156019 0.155995 0.058084
1 0.708073 0.020584 0.969910 0.832443 0.212339 0.181825 0.183405
2 0.431945 0.291229 0.611853 0.139494 0.292145 0.366362 0.456070
3 0.514234 0.592415 0.046450 0.607545 0.170524 0.065052 0.948886
4 0.304614 0.097672 0.684233 0.440152 0.122038 0.495177 0.034389
C2 C3 A_result B_result C_result
0 0.866176 0.601115 1.670611 0.626796 1.025551
1 0.304242 0.524756 1.620915 0.883542 0.420470
2 0.785176 0.199674 0.745815 0.274016 1.080532
3 0.965632 0.808397 0.865290 0.636899 2.409623
4 0.909320 0.258780 0.634494 0.576463 0.878582
您可以使用自己的函数替换power
中的func_dict
函数,例如将值与其他值相加或使用它们执行某种奇特的统计计算。
将此与我之前编辑的管道方法结合使用,可以为您提供获得所需结果的工具。