我想计算并减去列子集的平均值。这是一种方法:
#!/usr/bin/env python3
import numpy as np
import pandas as pd
def col_avg(df, col_ids):
'''Calculate and subtract average over *col_ids*
*df* is modified in-place.
'''
cols = [ df.columns[i] for i in col_ids ]
acc = df[cols[0]].copy()
for col in cols[1:]:
acc += df[col]
acc /= len(cols)
for col in cols:
df[col] -= acc
# Create example data
np.random.seed(42)
df = pd.DataFrame(data=np.random.random((433,80)) + np.arange(433)[:, np.newaxis],
columns=['col-%d' % x for x in range(80)])
#df = pd.DataFrame.from_csv('data.csv')
# Calculate average over columns 2, 3 and 6
df_old = df.copy()
col_avg(df, [ 1, 2, 5])
assert any(df_old.iloc[0] != df.iloc[0])
现在,我并不特别喜欢这两个for循环,所以我试着更简洁地表达同样的操作:
def col_avg(df, col_ids):
dfT = df.T
mean = dfT.iloc[col_ids].mean()
dfT.iloc[col_ids] -= mean
这种实现看起来更好(IMO),但它有一个缺点:它只适用于某些数据集。通过上面的例子,它可以工作。但是例如加载this csv file时失败。
我唯一的解释是,在某些情况下,dfT.iloc[col_ids]
表达式必须在内部创建值数组的副本,而不是就地修改它。
编辑:在建议替代实施时,请说明您认为实施始终的原因。毕竟,上面的代码似乎也适用于某些输入。
答案 0 :(得分:1)
DataFrame的转置dfT = df.T
可能会返回一个新的DataFrame,而不是一个视图。
在这种情况下,修改dfT
对df
无效。
在您的玩具示例中,
df = pd.DataFrame(data=np.random.random((433,80)) + np.arange(433)[:, np.newaxis],
columns=['col-%d' % x for x in range(80)])
所有列都具有相同的dtype:
In [83]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 433 entries, 0 to 432
Data columns (total 80 columns):
col-0 433 non-null float64
col-1 433 non-null float64
col-2 433 non-null float64
...
dtypes: float64(80)
memory usage: 274.0 KB
而在使用CSV构建的DataFrame中,某些列具有int64
dtype:
In [55]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 492 entries, 0 to 491
Data columns (total 72 columns):
sample no 492 non-null int64
index 492 non-null int64
plasma-r 492 non-null float64
plasma-z 492 non-null float64
...
DataFrame的列始终只有一个dtype。所以当你转置它时
基于CSV的df
,无法通过简单地转置a来形成新的DataFrame
单个底层NumPy数组。整数列中的整数
现在遍布各行。 df.T
的每个列必须有一个dtype,所以
整数被上浮到浮点数。所以df.T
的所有列都有dtype
float64
。 dtypes更改时必须复制数据。
底线是:因此当df
具有混合类型时,df.T
是副本。
col_avg
可以简化为
def col_avg2(df, col_ids):
means = df.iloc[:, col_ids].mean(axis=1)
for i in col_ids:
df.iloc[:, i] -= means
请注意,表达式 df.iloc[:, col_ids]
将返回一个副本,因为cols_ids
不是基本切片。但分配到df.iloc[...]
(或df.loc[...]
)可以保证修改df
。
这就是为什么分配df.iloc
或df.loc
是避免assignment-with-chained-indexing pitfall的推荐方法。
答案 1 :(得分:0)
根据我对这个问题的理解,这就是你所要求的。我不明白你为什么要转置数据帧。注意:为简单起见,我删除了字符串列名称,但您可以轻松地替换它们。
np.random.seed(42)
df = pd.DataFrame(data=np.random.random((6,8)) + np.arange(6)[:, np.newaxis])#,
#columns=['col-%d' % x for x in range(80)])
# Calculate average over columns 2, 3 and 6
df_old = df.copy()
col_ids=[1,2,5]
df[col_ids] = df[col_ids] - np.mean(df[col_ids].values)
df_old-df # to make sure average is calculated over all three columns
Out[139]:
0 1 2 3 4 5 6 7
0 0 2.950637 2.950637 0 0 2.950637 0 0
1 0 2.950637 2.950637 0 0 2.950637 0 0
2 0 2.950637 2.950637 0 0 2.950637 0 0
3 0 2.950637 2.950637 0 0 2.950637 0 0
4 0 2.950637 2.950637 0 0 2.950637 0 0
5 0 2.950637 2.950637 0 0 2.950637 0 0
答案 2 :(得分:0)
OP,假设您希望计算相似列的平均值并减去(比如&#34; Psi在......&#34;列)。最简单的方法是
df = pd.read_csv('data.csv')
psi_cols = [c for c in df.columns if c.startswith('Psi')]
df[psi_cols] -= df[psi_cols].mean().mean()
这计算所有列的总平均值。如果要从每列中减去列平均值,请执行
df[psi_cols] -= df[psi_cols].mean()