我不是在寻找合并/连接列或用其他值替换某些值(虽然......也许是吗?)。但我有一个大的数据帧(> 100行和列),我想提取“几乎相同”的列,即共同具有> 2值(在相同的索引处)并且在其他值上没有不同的值索引(如果一列中有值,则必须有相同的值或另一列中的NaN)。 以下是此类数据框的示例:
a = np.random.randint(1,10,10)
b = np.array([np.nan,2,np.nan,3,np.nan,6,8,1,2,np.nan])
c = np.random.randint(1,10,10)
d = np.array([7,2,np.nan,np.nan,np.nan,6,8,np.nan,2,2])
e = np.array([np.nan,2,np.nan,np.nan,np.nan,6,8,np.nan,np.nan,2])
f = np.array([np.nan,2,np.nan,3.0,7,np.nan,8,np.nan,np.nan,2])
df = pd.DataFrame({'A':a,'B':b,'C':c,'D':d,'E':e, 'F':f})
df.ix[3:6,'A']=np.nan
df.ix[4:8,'C']=np.nan
修改
keys=['S01_o4584','S02_o2531','S03_o7812','S03_o1122','S04_o5210','S04_o3212','S05_o4665','S06_o7425','S07_o3689','S08_o2371']
df['index']=keys
df = df.set_index('index')
A B C D E F
index
S01_o4584 8.0 NaN 9.0 7.0 NaN NaN
S02_o2531 8.0 2.0 5.0 2.0 2.0 2.0
S03_o7812 1.0 NaN 5.0 NaN NaN NaN
S03_o1122 NaN 3.0 6.0 NaN NaN 3.0
S04_o5210 NaN NaN NaN NaN NaN 7.0
S04_o3212 NaN 6.0 NaN 6.0 6.0 NaN
S05_o4665 NaN 8.0 NaN 8.0 8.0 8.0
S06_o7425 1.0 1.0 NaN NaN NaN NaN
S07_o3689 8.0 2.0 NaN 2.0 NaN NaN
S08_o2371 3.0 NaN 9.0 2.0 2.0 2.0
如您所见,列B,D (和新E)在位置(索引)S02_o2531,S04_o3212,S05_o4665和S08_o2371处具有相同的值,而在其他位置,一个具有值,而在其他人有NaN。
我想要的输出是:
index BD*E*
S01_o4584 7
S02_o2531 2
S03_o7812 NaN
S03_o1122 3
S04_o5210 NaN
S04_o3212 6
S05_o4665 8
S06_o7425 1
S07_o3689 2
S08_o2371 2
但是,我无法组合那些对于索引的同一个开头有两个不同值的列:正如您所看到的,列F也共享一些索引,但新的索引位于S04_o5210,但是先前的组合列已经具有“S04_”的值(索引S04_o3212)。
有合理的pythonic方式吗?即1)根据条件中的列找到列,它们中的值必须相同或者np.nan,而不是不同。 2)设置一个条件,如果列具有与先前包含的值的索引相同的开头,则不能组合列(我可能需要将字符串拆分为两列并进行多索引)3)将它们组合成新的系列/数据帧。
答案 0 :(得分:1)
def almost(df):
i, j = np.triu_indices(len(df.columns), 1)
v = df.values
d = v[:, i] - v[:, j]
m = (np.where(np.isnan(d), 0, d) == 0).all(0)
return pd.concat(
[
df.iloc[:, i_].combine_first(
df.iloc[:, j_]
).rename(
tuple(df.columns[[i_, j_]])
) for i_, j_ in zip(i[m], j[m])],
axis=1
)
almost(df)
B
D
0 7.0
1 2.0
2 NaN
3 3.0
4 NaN
5 6.0
6 8.0
7 1.0
8 2.0
9 2.0
如何运作
i
和j
使用numpy
代表每个列的组合,以获取上三角的索引。numpy
和df.values
对基础i
数组j
进行切片并减去它们。如果差异为nan
,则表示其中一个是nan
。否则,如果各个元素相同,则差值应为零。nan
,所以使用np.where
将其填充为零。(x == 0).all(0)
查找所有行的归零位置。i
和j
进行切片,并确定匹配的列。pd.MultiIndex
为所有匹配项构建一个数据框,用于显示匹配内容的列。酷示例
np.random.seed([3,1415])
m, n = 20, 26
df = pd.DataFrame(
np.random.randint(10, size=(m, n)),
columns=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
).mask(np.random.choice([True, False], (m, n), p=(.6, .4)))
df
almost(df)
A D G H I J K
J X K M N J K V S X
0 6.0 7.0 3.0 NaN 4.0 6.0 NaN 6.0 NaN 7.0
1 3.0 3.0 2.0 6.0 4.0 NaN 2.0 6.0 2.0 2.0
2 3.0 0.0 NaN 2.0 4.0 3.0 NaN 3.0 4.0 0.0
3 4.0 4.0 3.0 5.0 5.0 4.0 3.0 4.0 3.0 3.0
4 7.0 NaN NaN 7.0 3.0 7.0 NaN 7.0 NaN NaN
5 NaN NaN 2.0 0.0 5.0 NaN 2.0 2.0 2.0 2.0
6 NaN 8.0 NaN NaN 9.0 2.0 2.0 1.0 NaN 8.0
7 NaN 7.0 NaN 9.0 9.0 6.0 6.0 NaN NaN 7.0
8 NaN NaN 8.0 3.0 1.0 NaN NaN NaN 4.0 NaN
9 0.0 0.0 8.0 2.0 NaN 3.0 3.0 NaN NaN NaN
10 0.0 0.0 NaN 6.0 1.0 NaN NaN 8.0 NaN NaN
11 NaN NaN 3.0 NaN 9.0 3.0 3.0 NaN 3.0 3.0
12 5.0 NaN NaN NaN 6.0 5.0 NaN 5.0 8.0 NaN
13 NaN NaN NaN NaN 7.0 5.0 5.0 NaN NaN NaN
14 NaN NaN 6.0 4.0 8.0 8.0 8.0 NaN 0.0 NaN
15 8.0 8.0 7.0 NaN NaN NaN NaN NaN 2.0 NaN
16 4.0 4.0 4.0 4.0 9.0 9.0 9.0 6.0 4.0 NaN
17 NaN 4.0 NaN 4.0 2.0 8.0 8.0 4.0 NaN 4.0
18 NaN NaN 2.0 7.0 NaN NaN NaN NaN NaN NaN
19 NaN 7.0 6.0 3.0 5.0 NaN NaN 7.0 NaN 7.0
答案 1 :(得分:1)
听起来,关键是如何检测"几乎相同的"列,这些列只是缺少哪些值(如果有的话)。给定两个列名称,如何检查它们是否几乎相同?请注意,如果我们发现一个重要的差异,则它必须位于两列都没有NaN
的索引处。换句话说,诀窍是丢弃具有缺失值的行并比较其余行:
tocheck = df[["B", "D"]].dropna()
if all(tocheck.B == tocheck.D):
print("B, D are almost identical")
让我们使用它迭代所有列对,并合并匹配的列:
for a, b in itertools.combinations(df.columns, 2):
if a not in df.columns or b not in df.columns: # Was one deleted already?
continue
tocheck = df[[a, b]].dropna()
if all(tocheck[a] == tocheck[b]):
print(b, "->", a)
df[a] = df[a].combine_first(df[b])
del df[b]
注意(如果您没有注意到),当多列最终合并时,可能会有依赖于订单的行为。例如:
A B C
0 NaN 1 2
1 10 NaN NaN
您可以将B
或C
合并到A
,但不能同时合并到 = f.collection_select :item_ids, Item.order("name ASC").all, :id, :name, {}, {:multiple => true, class: "form-control"}
。除了这些问题之外,可以将多个列合并为一个,因为合并的列将保存在其中一个比较列中。
答案 2 :(得分:0)
et voila
test = df.B == df.D
df.loc[test,'myunion'] = df.loc[test, 'B']
df.loc[!test ,'myunion'] = df.loc[!test, 'B'].fillna(0) + df.loc[!test, 'D'].fillna(0)