用熊猫解析漂亮打印的表格数据

时间:2019-12-06 10:48:34

标签: python pandas clipboard copy-paste

复制包含不同分隔符,列名称中的空格等的表的最佳方法是什么。函数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

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

4 个答案:

答案 0 :(得分:6)

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

这并不意味着不可能用它来转化成熊猫!让我们从.read_clipboard()开始:

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

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

.iloc[1:,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 文件中,并使用.read_csv()提供的更高级的功能。但是,必要的转换将保持不变。

答案 1 :(得分:5)

这是使用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 :(得分:4)

一个选择是咬紧牙关,然后预处理您的数据。这还不是很糟糕,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

答案 3 :(得分:3)

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

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

然后需要进行最小程度的清理:

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

至于“编写这样的电子表格”问题...我看不出有什么比普通演示文稿更方便的了,但是鉴于上述已清除的df,这是不好的代码:< / p>

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   |

(已编辑)

相关问题