【问题标题】:Parse prettyprinted tabular data with pandas使用 pandas 解析漂亮打印的表格数据
【发布时间】:2020-03-31 08:37:59
【问题描述】:

复制包含不同分隔符、列名中的空格等的表的最佳方法是什么。函数pd.read_clipboard() 无法单独管理此任务。

示例 1:

| Age Category | A | B  | C  | D |
|--------------|---|----|----|---|
| 21-26        | 2 | 2  | 4  | 1 |
| 26-31        | 7 | 11 | 12 | 5 |
| 31-36        | 3 | 5  | 5  | 2 |
| 36-41        | 2 | 4  | 1  | 7 |
| 41-46        | 0 | 1  | 3  | 2 |
| 46-51        | 0 | 0  | 2  | 3 |

预期结果:

 Age Category  A  B   C   D    
 21-26         2  2   4   1 
 26-31         7  11  12  5 
 31-36         3  5   5   2 
 36-41         2  4   1   7 
 41-46         0  1   3   2 
 46-51         0  0   2   3

编辑:

示例 2:

+---+---------+--------+
| id|firstName|lastName|
+---+---------+--------+
|  1|     Mark|   Brown|
|  2|      Tom|Anderson|
|  3|   Joshua|Peterson|
+---+---------+--------+

预期结果:

   id firstName  lastName
0   1      Mark     Brown
1   2       Tom  Anderson
2   3    Joshua  Peterson

我寻找一种可以应用于最常见表格类型的通用方法。

【问题讨论】:

  • @ldz 没有。这个表有问题。
  • 我看到其他人使用StringIO,但我也很好奇。谢谢你的好问题
  • @Datanovice pd.read_clipboard('|').dropna(how='all',axis=1).iloc[1:].reset_index(drop=True) ?
  • 感谢@anky_91,代码不是真正的问题,我想知道是否有更好的方法来处理这样的字符串表,而不需要像上面那样编写代码,但正如 jorijnsmit 指出的那样更多的是为了视觉表现。

标签: python pandas clipboard copy-paste


【解决方案1】:

这是另一个使用 re.subio.StringIO 的潜在解决方案:

from io import StringIO
import re

text1 = """
| Age Category | A | B  | C  | D |
|--------------|---|----|----|---|
| 21-26        | 2 | 2  | 4  | 1 |
| 26-31        | 7 | 11 | 12 | 5 |
| 31-36        | 3 | 5  | 5  | 2 |
| 36-41        | 2 | 4  | 1  | 7 |
| 41-46        | 0 | 1  | 3  | 2 |
| 46-51        | 0 | 0  | 2  | 3 |
"""

text2= """
+---+---------+--------+
| id|firstName|lastName|
+---+---------+--------+
|  1|     Mark|   Brown|
|  2|      Tom|Anderson|
|  3|   Joshua|Peterson|
+---+---------+--------+
"""

df1 = pd.read_csv(StringIO(re.sub(r'[|+]|-{2,}', '  ', text1)), sep='\s{2,}', engine='python')
df2 = pd.read_csv(StringIO(re.sub(r'[|+]|-{2,}', '  ', text2)), sep='\s{2,}', engine='python')

[出]

df1

  Age Category  A   B   C  D
0        21-26  2   2   4  1
1        26-31  7  11  12  5
2        31-36  3   5   5  2
3        36-41  2   4   1  7
4        41-46  0   1   3  2
5        46-51  0   0   2  3

df2

   id firstName  lastName
0   1      Mark     Brown
1   2       Tom  Anderson
2   3    Joshua  Peterson

【讨论】:

    【解决方案2】:

    对于这种类型的表,你可以简单地使用:

    df = pd.read_clipboard(sep='|')
    

    然后需要进行最少的清理:

    df = df.drop(0)
    df = df.drop(['Unnamed: 0','Unnamed: 6'], axis=1)
    

    至于“编写这样一个电子表格”的问题...我看不出有什么比简单的演示更方便的了,但是考虑到上面已清理的df,这里的代码很糟糕:

    df1 = df.append(pd.DataFrame({i:['-'*len(i)] for i in df.columns})).sort_index() #adding the separator to column titles
    df2 = pd.DataFrame({str(i)+'|':['|']*len(df1) for i in range(len(df1.columns))})
    df3 = df1.join(df2)
    col_order = [j for i in [[df1.columns[x], df2.columns[x]] for x in range(len(df1.columns))] for j in i]
    df3.index = ['|']*len(df3.index)
    

    然后:

    df3[col_order]
    
        Age Category  0|   A  1|   B   2|   C   3|   D  4|
    |  --------------  |  ---  |  ----  |  ----  |  ---  |
    |   21-26          |   2   |   2    |   4    |   1   |
    |   26-31          |   7   |   11   |   12   |   5   |
    |   31-36          |   3   |   5    |   5    |   2   |
    |   36-41          |   2   |   4    |   1    |   7   |
    |   41-46          |   0   |   1    |   3    |   2   |
    |   46-51          |   0   |   0    |   2    |   3   |
    

    (已编辑)

    【讨论】:

      【解决方案3】:

      一种选择是硬着头皮对数据进行预处理。这还不算太糟糕,pd.read_csv 在它的参数中只能处理这么多的情况,如果你想详尽地处理你处理的情况,你最终会转向正则表达式。

      为了处理漂亮打印表的大多数常见情况,我只需编写一个循环来过滤/替换行中的字符,然后使用相对简单的read_csv 调用读取输出。

      import os 
      
      def load(filename):
          with open(filename) as fin, open('temp.txt', 'w') as fout:
              for line in fin:
                  if not line.strip()[:2] in {'|-', '+-'}: # filter step
                      fout.write(line.strip().strip('|').replace('|', ',')+'\n')
      
          df = pd.read_csv('temp.txt', sep=r'\s*,\s*', engine='python')
          os.unlink('temp.txt') # cleanup
      
          return df
      

      df1 = load('data1.txt')
      df2 = load('data2.txt')
      
      df1
      
        Age Category  A   B   C
      0        21-26  2   2   4
      1        26-31  7  11  12
      2        31-36  3   5   5
      3        36-41  2   4   1
      4        41-46  0   1   3
      5        46-51  0   0   2
      
      df2
      
         id firstName  lastName
      0   1      Mark     Brown
      1   2       Tom  Anderson
      2   3    Joshua  Peterson
      

      【讨论】:

      • @Datanovice 不用担心。你的意思是把赏金奖励给另一个答案吗?我不介意,只是好奇。我知道这不是一个完美的答案,但我不相信这个问题有一个。
      • 如果我造成任何冒犯,我深表歉意,我的意图是有更多的解释你的答案是正确的,但我想更多地了解为什么这些表格存在或者是否有我不知道的阅读方法。
      • @Datanovice 回复:为什么这些表存在,它们可能是 SQL 或 Spark 表转储,或者它们是 tabulate prettyprinted tables
      【解决方案4】:

      之所以如此复杂,是因为这些类型的 ASCII 表在设计时并没有真正考虑到数据传输。它们的真正功能是以视觉上令人愉悦的方式描述数据。

      这并不意味着不能用它转入pandas!让我们从.read_clipboard()开始:

      df = pd.read_clipboard(sep='|').iloc[1:,1:-1]
      

      我们将| 定义为分隔符,而不是使用逗号作为(默认)分隔符。

      .iloc[1:,1:-1] 删除了第一行 (-----------) 以及第一列和最后一列:因为每行开头和结尾处的尾随 | pandas 在那里看到一个“空”列.

      现在剩下的就是从列名和值中去除空格:

      stripped_columns = []
      for column_name in df.columns:
          df[column_name] = df[column_name].str.strip()
          stripped_columns.append(column_name.strip())
      df.columns = stripped_columns
      

      如果您希望 Age Category 成为您的索引:

      df.set_index('Age Category', inplace=True)

      我要做的最后一步是确保您的所有列现在实际上都包含数字而不是字符串:

      df = df.astype('int')
      

      导致:

      <class 'pandas.core.frame.DataFrame'>
      Index: 6 entries, 21-26 to 46-51
      Data columns (total 4 columns):
      A    6 non-null int64
      B    6 non-null int64
      C    6 non-null int64
      D    6 non-null int64
      dtypes: int64(4)
      memory usage: 400.0+ bytes
      

      我不确定您从剪贴板读取它的原因是什么。更优雅的解决方案可能是将其粘贴到 .csv 文件中并使用 .read_csv() 必须提供的更高级的功能。然而,必要的转换将保持不变。

      【讨论】:

      • 如果 SQL 或 Python 都无法直接读取而无需额外处理,为什么 ---- 在所有表中如此普遍?
      • 我会这么说,因为它的真正目的不是将数据从一个环境传输到另一个环境,而是对其进行可视化描述。
      猜你喜欢
      • 2012-03-31
      • 2021-06-22
      • 2013-09-29
      • 2019-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-07
      相关资源
      最近更新 更多