Python 3.x 基于标志熔化数据帧

Python 3.x 基于标志熔化数据帧,python-3.x,pandas,dataframe,Python 3.x,Pandas,Dataframe,我有这样一个数据帧: pd.DataFrame({'time':['01-01-2020','02-01-2020','01-01-2020','02-01-2020'],'level':['q','q','r','r'],'a':[1,2,3,4],'b':[12,34,54,67],'c':[18,29,39,47],'a_1':[0.1,0.2,0.3,0.4],'a_2':[0,1,0,1],'b_1':[0.28,0.47,0.02,0.05],'b_2':[1,1,0,1],'c_1

我有这样一个数据帧:

pd.DataFrame({'time':['01-01-2020','02-01-2020','01-01-2020','02-01-2020'],'level':['q','q','r','r'],'a':[1,2,3,4],'b':[12,34,54,67],'c':[18,29,39,47],'a_1':[0.1,0.2,0.3,0.4],'a_2':[0,1,0,1],'b_1':[0.28,0.47,0.02,0.05],'b_2':[1,1,0,1],'c_1':[0.18,0.40,0.12,0.01],'c_2':[1,1,0,0]})
>>  time      level a   b   c   a_1 a_2  b_1   b_2  c_1   c_2
0   01-01-2020  q   1   12  18  0.1 0   0.28    1   0.18    1
1   02-01-2020  q   2   34  29  0.2 1   0.47    1   0.40    1
2   01-01-2020  r   3   54  39  0.3 0   0.02    0   0.12    0
3   02-01-2020  r   4   67  47  0.4 1   0.05    1   0.01    0
我希望将数据与
time
level
合并为索引,并将所有其他列作为行,这些行的前缀对应一个标志1。例如,我希望将
a
a_1
的值列为
项目
,如果
a_2
的值为1。 期望输出:

>>   time   level column values items
0   01-01-2020  q   b   12  0.28
1   01-01-2020  q   c   18  0.18
2   02-01-2020  q   a   2   0.20
3   02-01-2020  q   b   34  0.47
4   02-01-2020  q   c   29  0.40
5   02-01-2020  r   a   4   0.40
6   02-01-2020  r   b   67  0.05

我可以得到所有的值,而不考虑标志,然后过滤
flags==1
。但是,不确定在这种情况下如何“融化”/“取消堆叠”。我试了很多方法,但都没有成功。请帮帮我。

也许有一种更优雅的方式,但这很管用。提取每个列名(a、b、c)的数据,选择那些标志设置为1的列名并连接结果

df.set_index(['time', 'level'], inplace=True)
parts = []
for name in 'a','b','c':
    d = df[[name, f'{name}_1', f'{name}_2']]\
         .rename(columns={name: 'values', f'{name}_1': 'items', f'{name}_2': 'flag'})
    d['column'] = name
    parts.append(d[d.flag == 1])
pd.concat(parts)[['column','values','items']].reset_index()
让我们尝试一下:

详细信息:

mask
以后缀
\u 1结尾的列中的值,其中相应标志列中的值等于
0

   a_1   b_1   c_1
0  NaN  0.28  0.18
1  0.2  0.47  0.40
2  NaN   NaN   NaN
3  0.4  0.05   NaN
melt
包含列
a、b、c
的数据框,然后
重塑
屏蔽值,并在熔化的数据框中指定新列

          time level columns  value  items
0   01-01-2020     q       a      1    NaN
1   02-01-2020     q       a      2   0.20
2   01-01-2020     r       a      3    NaN
3   02-01-2020     r       a      4   0.40
4   01-01-2020     q       b     12   0.28
5   02-01-2020     q       b     34   0.47
6   01-01-2020     r       b     54    NaN
7   02-01-2020     r       b     67   0.05
8   01-01-2020     q       c     18   0.18
9   02-01-2020     q       c     29   0.40
10  01-01-2020     r       c     39    NaN
11  02-01-2020     r       c     47    NaN
最后,在
项目中删除
NaN
值,并对
时间和
级别的值进行排序,以获得最终结果

         time level columns  value  items
0  01-01-2020     q       b     12   0.28
1  01-01-2020     q       c     18   0.18
2  02-01-2020     q       a      2   0.20
3  02-01-2020     q       b     34   0.47
4  02-01-2020     q       c     29   0.40
5  02-01-2020     r       a      4   0.40
6  02-01-2020     r       b     67   0.05

步骤1:对列重新排序,使数字位于字母之前:

res = df.copy()
res.columns = ["_".join(entry.split("_")[::-1]) for entry in res]
步骤2:对列重新排序(再次),如果列位于(“a”、“b”、“c”)中,则以“num”为前缀

步骤3:使用pandas重塑数据,筛选等于1的行,重命名列,最后重置索引:

(
    pd.wide_to_long(
        res,
        stubnames=["num", "1", "2"],
        i=["time", "level"],
        j="column",
        sep="_",
        suffix=".",
    )
     # this is where the filter for rows equal to 1 occur
    .query("`2`==1")
    .drop(columns="2")
    .set_axis(["values", "items"], axis="columns")
    .reset_index()
)


time    level   column  values  items
0   01-01-2020  q   b   12  0.28
1   01-01-2020  q   c   18  0.18
2   02-01-2020  q   a   2   0.20
3   02-01-2020  q   b   34  0.47
4   02-01-2020  q   c   29  0.40
5   02-01-2020  r   a   4   0.40
6   02-01-2020  r   b   67  0.05
这是另一种方法,但重命名列的想法相同-使用以下工具可以轻松重塑形状:


非常感谢你!!我投了更高的票。我也会等别人回答,看看有没有更优雅的方法;如果没有,我会接受这个答案。谢谢你的回答@sammywemmy。它工作得很好!
res.columns = [f"num_{letter}" if letter in ("a", "b", "c") 
               else letter 
               for letter in res]
res

time    level   num_a   num_b   num_c   1_a 2_a 1_b 2_b 1_c 2_c
0   01-01-2020  q   1   12  18  0.1 0   0.28    1   0.18    1
1   02-01-2020  q   2   34  29  0.2 1   0.47    1   0.40    1
2   01-01-2020  r   3   54  39  0.3 0   0.02    0   0.12    0
3   02-01-2020  r   4   67  47  0.4 1   0.05    1   0.01    0
(
    pd.wide_to_long(
        res,
        stubnames=["num", "1", "2"],
        i=["time", "level"],
        j="column",
        sep="_",
        suffix=".",
    )
     # this is where the filter for rows equal to 1 occur
    .query("`2`==1")
    .drop(columns="2")
    .set_axis(["values", "items"], axis="columns")
    .reset_index()
)


time    level   column  values  items
0   01-01-2020  q   b   12  0.28
1   01-01-2020  q   c   18  0.18
2   02-01-2020  q   a   2   0.20
3   02-01-2020  q   b   34  0.47
4   02-01-2020  q   c   29  0.40
5   02-01-2020  r   a   4   0.40
6   02-01-2020  r   b   67  0.05
result = df.rename(
    columns=lambda x: f"values_{x}"
    if x in ("a", "b", "c")
    else f"items_{x[0]}"
    if re.search(".1$", x)
    else f"equals1_{x[0]}"
    if re.search(".2$", x)
    else x
)


(
    pd.wide_to_long(
        result,
        stubnames=["values", "items", "equals1"],
        i=["time", "level"],
        j="column",
        sep="_",
        suffix=".",
    )
    .query("equals1==1")
    .iloc[:, :-1]
    .reset_index()
)