【问题标题】:Python: Compare String to whole next columnPython:将字符串与整个下一列进行比较
【发布时间】:2017-06-19 12:48:06
【问题描述】:

我有以下数据框:

df1:
       2000 2001 2002 
        a    a     a 
        b    b     c
        c    c     d

因此,在 2002 年,值 b 被 c 取代。我现在想要的是对于每一列,检查列的每个值,即分别针对 a、b 和 c,是否是下一列的成员。然后,我想要 a、b、c 和 d 的输出,指示字符串成为第一个成员的日期以及它停止成为成员的日期。像这样:

df2: 
      a       b       c     d
      2000   2000   2000   2002
      NaN    2002   Nan    NaN

谁能帮助我如何在 python 中实现这一点?

编辑:这是我开始的方式,但我不知道如何在 python 中实现它。

 for c in columns: 
     for v in column:
         drop v if v is not in c_[+1] 
 remove all empty columns        

理论上,这应该给我一个数据框,只显示带有已删除值的日期。例如:

  df2: 
  2002 
   b

然后我会做一个类似的分析,但对于添加的值,然后合并两个数据框。但是,我不知道如何将每一列准确地转换为一个列表并检查 v 是否是该列表的一部分,然后转到下一列。

【问题讨论】:

  • 您有解决方案的尝试吗?
  • 这是做作业的吗?不知道我是否应该为你写一个算法..
  • 不,这不是作业。我在上面添加了一个尝试

标签: python pandas string-comparison


【解决方案1】:

查看这里的有用工具是pd.DataFrame().stack()

df1.stack()
Out[24]: 
0  2000    a
   2001    a
   2002    a
1  2000    b
   2001    b
   2002    c
2  2000    c
   2001    c
   2002    d
dtype: object

因为你的列名排序很好,你可以排序,然后使用drop_duplicates()得到:

df1.stack().sort_index(level=1).drop_duplicates()
Out[26]: 
0  2000    a
1  2000    b
2  2000    c
   2002    d
dtype: object

df1.stack().sort_index(level=1).drop_duplicates(keep='last')
Out[28]: 
1  2001    b
0  2002    a
1  2002    c
2  2002    d
dtype: object

要将这些转换为按值索引的年份,而不是按年份的值,您可以将.reset_index().set_index(0)['level_1'] 添加到其中的任何一个:

start = df1.stack().sort_index(level=1).drop_duplicates().reset_index().set_index(0)['level_1']
start
Out[31]: 
0
a    2000
b    2000
c    2000
d    2002
Name: level_1, dtype: object

在为另一个也这样做之后,将其命名为end,您可以在从列构造的字典上使用pd.Series().map(),以获取第一个没有出现值的名称,而不是最后一个它在哪里。

cols = df1.columns.tolist()+[np.nan]
next_col = {cols[i]:cols[i+1] for i in range(len(cols)-1)}
end = end.map(next_col)
end
Out[36]: 
0
b    2002
a     NaN
c     NaN
d     NaN
Name: level_1, dtype: object

然后将这些组合起来创建df2,您可以使用pd.concat

df2 = pd.concat([start, end], axis=1).T.reset_index(drop=True)

df2
Out[40]: 
      a     b     c     d
0  2000  2000  2000  2002
1   NaN  2002   NaN   NaN

【讨论】:

  • 哇。这非常有效,但仅适用于起始值,即 df2 中的 0 索引。最终值始终是下一列。例如,我得到以下结果 a: 2000, 2001 b: 2000 2001, c: 2000, 2001.... 我对 end 和 start 执行相同的程序是否正确?因为在运行 start = df1.stack().sort_index(level=1).drop_duplicates().reset_index().set_index(0)['level_1'] 之后,我会执行 end = start 并执行其余的操作。
  • 错误可能是next_col = {cols[i]:cols[i+1] for i in range(len(cols)-1)} 中的`cols[I+1] 仅指下一个单元格。我可能需要一个 [last column] 命令,对吧?
  • endstart 几乎相同,只是在排序后保留最后一个副本而不是第一个副本。这就是end.drop_duplicates(keep='last') 中的keep='last' 参数的意义所在。您可以通过将ascending=False 传递给.sort_index()end 来获得类似的结果,尽管我认为最好提供将排序的堆叠数据帧存储为中间步骤的选项。 nextcol 应该只引用下一个单元格 - 否则,即使使用 keep='last'end 也会拉出显示值的最后一列,而不是不再显示值的第一列。
【解决方案2】:

通用算法:

1) 按年份将数据分组到列表中。 lzts = [['2000', 'a', 'b', 'c'], ['2001', 'a', 'b', 'c'], etc]

2) 创建函数来遍历列表,搜索给定值的实例。

def search(val):
  ans = (float('NaN'), float('NaN')) #start & end date for given value
  for lzt in lzts:
    if val in lzt[1:]: #skip first value since its the year
      if math.isnan(ans[0]): #no start date yet
        ans[0] = lzt[0] #add the year
    else: #value not found
      if not math.isnan(ans[0]): #already has start date 
        ans[1] = lzt[0] #add the year as end date

注意:此解决方案假定一旦某个值停止出现一年,它就永远消失了。如果某个值在一年内不出现然后返回,结果将不准确。

【讨论】:

    【解决方案3】:

    将每一列解析成一个列表,然后从那里开始。

    input = ''' 2000 2001 2002 
            a    a     a 
            b    b     c
            c    c     d '''
    
    lines = []
    for line in input.split('\n'):
        print ' '.join(line.split())
        lines.append(line.split())
    
    print lines
    

    输出:

    [['2000', '2001', '2002'], ['a', 'a', 'a'], ['b', 'b', 'c'], ['c', 'c', 'd']]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-05
      • 2022-06-14
      • 1970-01-01
      • 2021-03-04
      • 2021-08-27
      • 1970-01-01
      • 2016-01-16
      相关资源
      最近更新 更多