在Pandas DataFrame中的列表上使用numpy.where(或numpy.select)

时间:2019-03-04 17:11:37

标签: python pandas numpy

以下问题是对此的简化:Iterating through lists within a pandas DataFrame

我有一个包含一列列表的DataFrame:

import numpy as np
import pandas as pd

col = [["A", "B", "C", "D"], ["E", "F"]]
d = {"col" : [["A", "B", "C", "D"], ["E", "F"]]}

df = pd.DataFrame(d)

print(df)
Out[2]: 
            col
0  [A, B, C, D]
1        [E, F]

对于每一行,我要遍历列表并在以下情况下进行选择:

  • 列表的第一项(列表索引= 0):将列表的第一项写在第一列中
  • 在第一个条目和最后一个条目之间进行输入(列表索引= i):根据当前迭代,在列中写入当前条目和该条目之前的条目
  • 列表的最后一个条目(列表索引= -1):根据当前迭代将当前条目和该条目之前的条目写在列中,并根据当前迭代将列表的最后条目写入列中+1
  • 如果列表索引i大于列表的长度:根据当前迭代次数在列中写入np.nan

生成的DataFrame应该如下所示:

            col  0    1    2       3      4       5
0  [A, B, C, D]  A  B-A  C-B     D-C      D  np.nan
1        [E, F]  E  F-E    F  np.nan np.nan  np.nan

要获得此结果,我尝试了一个嵌套的numpy.where函数:

for i in range(7):
    df[i] = pd.DataFrame(np.where(i == 0,
                                  df["col"].apply(lambda x: x[0]),
                                  np.where(i == df["col"].apply(len),
                                           df["col"].apply(lambda x: x[-1]),
                                           np.where((i > 0) & (i <= df["col"].apply(len) - 1),
                                                    df["col"].apply(lambda x: x[i]) + '-' + df["col"].apply(lambda x: x[i-1]),
                                                    np.nan
                                                    )
                                           )
                                  )
                          )

print(df)

这是我的问题:我得到一个IndexError: list index out of range

我想这与i有关。 即使我发现i的无效情况,整个嵌套的term项都不成立。 (我也用numpy.select尝试过,但是得到了相同的结果。)

如果我将索引i替换为1,它会起作用(当然,它将给我错误的值,但我没有收到错误),因此它必须要做一些事情使用此索引,但我不知道如何解决该问题:

for i in range(7):
    df[i] = pd.DataFrame(np.where(i == 0,
                                  df["col"].apply(lambda x: x[0]),
                                  np.where(i == df["col"].apply(len),
                                           df["col"].apply(lambda x: x[-1]),
                                           np.where((i > 0) & (i <= df["col"].apply(len) - 1),
                                                    df["col"].apply(lambda x: x[1]) + '-' + df["col"].apply(lambda x: x[1-1]),
                                                    np.nan
                                                    )
                                           )
                                  )
                          )

print(df)

            col  0    1    2       3      4       5
0  [A, B, C, D]  A  B-A  B-A     B-A      D  np.nan
1        [E, F]  E  F-E    F  np.nan np.nan  np.nan

您能想到解决此问题的方法还是获得我想要的DataFrame的另一种方法?非常感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

我会将逻辑编码为一个单独的函数:

from typing import List

def compute_event_transitions(L: List[str]) -> pd.Series: 
    if len(L) <= 1:
        return pd.Series(L)

    first = pd.Series(L[0])
    last = pd.Series(L[-1])

    s1 = pd.Series(L)
    s2 = s1.shift(+1)

    middle = (
        pd.concat([s2, s1], axis='columns')
        [1:]  # The first element has no "from" transition
        .apply(lambda s: "-".join(s.tolist()), axis='columns')        
    )

    transitions = pd.concat([first, middle, last]).reset_index(drop=True)

    return transitions

现在,您可以将此计算应用于数据框中的每个元素:

all_transitions = df['col'].apply(compute_event_transitions)
   0    1    2    3    4
0  A  A-B  B-C  C-D    D
1  E  E-F    F  NaN  NaN

请注意,它的索引方式与原始数据帧相同,因此您可以将其缝合回列表列:

pd.concat([df, all_transitions], axis='columns')
            col  0    1    2    3    4
0  [A, B, C, D]  A  A-B  B-C  C-D    D
1        [E, F]  E  E-F    F  NaN  NaN