将具有特殊格式的文本文档转换为熊猫数据框架

共8个回答, 标签: python pandas

我有一个文本文件, 格式如下:

1: frack 0.733, shale 0.700,
10: space 0.645, station 0.327, nasa 0.258,
4: celebr 0.262, bahar 0.345

我需要隐藏此文本的数据框架具有以下格式:

Id   Term    weight
1    frack   0.733
1    shale   0.700
10   space   0.645
10   station 0.327
10   nasa    0.258
4    celebr  0.262
4    bahar   0.345

我怎么能做到呢?

第1个答案

下面是一种使用该文件进行分析的优化方法 re , 首先采用 id, 然后分析数据元组。 这利用了文件对象是可迭性的这一事实。 当您循环访问打开的文件时, 您将以字符串的方式获取各个行, 从中可以提取有意义的数据元素。

import re
import pandas as pd

SEP_RE = re.compile(r":\s+")
DATA_RE = re.compile(r"(?P[a-z]+)\s+(?P\d+\.\d+)", re.I)


def parse(filepath: str):
    def _parse(filepath):
        with open(filepath) as f:
            for line in f:
                id, rest = SEP_RE.split(line, maxsplit=1)
                for match in DATA_RE.finditer(rest):
                    yield [int(id), match["term"], float(match["weight"])]
    return list(_parse(filepath))

例子:

>>> df = pd.DataFrame(parse("/Users/bradsolomon/Downloads/doc.txt"),
...                   columns=["Id", "Term", "weight"])
>>>
>>> df
   Id     Term  weight
0   1    frack   0.733
1   1    shale   0.700
2  10    space   0.645
3  10  station   0.327
4  10     nasa   0.258
5   4   celebr   0.262
6   4    bahar   0.345

>>> df.dtypes
Id          int64
Term       object
weight    float64
dtype: object

演练

SEP_RE查找初始分隔符: 文本 : 后跟一个或多个空格。 maxsplit=1一旦找到第一个拆分, 它就会停止。 当然, 这假定您的数据是严格的格式化的: 整个数据集的格式始终遵循您的问题中列出的示例格式。

之后, DATA_RE.finditer() 处理每个 (术语、重量) 对. rest 字符串 rest 本身将看起来像 frack 0.733, shale 0.700,.finditer()为您提供多个 match 对象, 您可以在其中 ["key"] 使用表示法从给定的命名捕获组访问元素, 例如 (?P[a-z]+)

可视化的一种简单方法是将 line 文件中的示例用作字符串:

>>> line = "1: frack 0.733, shale 0.700,\n"
>>> SEP_RE.split(line, maxsplit=1)
['1', 'frack 0.733, shale 0.700,\n']

现在, 您有了初始 ID 和组件的其余部分, 您可以将其解压缩到两个标识符中。

>>> id, rest = SEP_RE.split(line, maxsplit=1)
>>> it = DATA_RE.finditer(rest)
>>> match = next(it)
>>> match

>>> match["term"]
'frack'
>>> match["weight"]
'0.733'

将其可视化的更好方法是 pdb 。 如果你敢;) 就试试吧

免责 声明

这是需要特定类型的解决方案的问题之一, 如果您放松对数据格式的限制, 这些解决方案可能无法很好地概括。

例如, 它假定每个 Term 字母只能使用大写或小写 ascii 字母, 而不能使用其他字母。 如果您有其他 Unicode 字符作为标识符, 则需要查找其他 re 字符 \w , 例如。

第2个答案

如果将输入按摩到适当的格式, 则可以使用 DataFrame 构造函数。这里有一个方法:

import pandas as pd
from itertools import chain

text="""1: frack 0.733, shale 0.700,
10: space 0.645, station 0.327, nasa 0.258,
4: celebr 0.262, bahar 0.345 """

df = pd.DataFrame(
    list(
        chain.from_iterable(
            map(lambda z: (y[0], *z.strip().split()), y[1].split(",")) for y in
            map(lambda x: x.strip(" ,").split(":"), text.splitlines())
        )
    ),
    columns=["Id", "Term", "weight"]
)

print(df)
#  Id     Term weight
#0  4    frack  0.733
#1  4    shale  0.700
#2  4    space  0.645
#3  4  station  0.327
#4  4     nasa  0.258
#5  4   celebr  0.262
#6  4    bahar  0.345

解释

我想你已经把你的文件读进了字符串 text 。你想做的第一件事是在拆分之前剥离前导行/尾随逗号和空格:

print(list(map(lambda x: x.strip(" ,").split(":"), text.splitlines())))
#[['1', ' frack 0.733, shale 0.700'],
# ['10', ' space 0.645, station 0.327, nasa 0.258'],
# ['4', ' celebr 0.262, bahar 0.345']]

下一步是在逗号上拆分以分隔值, 并 Id 为每组值分配:

print(
    [
        list(map(lambda z: (y[0], *z.strip().split()), y[1].split(","))) for y in
        map(lambda x: x.strip(" ,").split(":"), text.splitlines())
    ]
)
#[[('1', 'frack', '0.733'), ('1', 'shale', '0.700')],
# [('10', 'space', '0.645'),
#  ('10', 'station', '0.327'),
#  ('10', 'nasa', '0.258')],
# [('4', 'celebr', '0.262'), ('4', 'bahar', '0.345')]]

最后, 我们使用 itertools.chain.from_iterable 扁平此输出, 然后可以将其直接传递给 DataFrame 构造函数。

注意: * 元组解包是巨蟒 3 功能。

第3个答案

假设您的数据 ( csv 文件) 看起来像给定的:

df = pd.read_csv('untitled.txt', sep=': ', header=None)
df.set_index(0, inplace=True)

# split the `,`
df = df[1].str.strip().str.split(',', expand=True)

#    0             1              2           3
#--  ------------  -------------  ----------  ---
# 1  frack 0.733   shale 0.700
#10  space 0.645   station 0.327  nasa 0.258
# 4  celebr 0.262  bahar 0.345

# stack and drop empty
df = df.stack()
df = df[~df.eq('')]

# split ' '
df = df.str.strip().str.split(' ', expand=True)

# edit to give final expected output:

# rename index and columns for reset_index
df.index.names = ['Id', 'to_drop']
df.columns = ['Term', 'weight']

# final df
final_df  = df.reset_index().drop('to_drop', axis=1)
第4个答案

只是为了把我的两分钱: 你可以给自己写一个解析器, 并把结果输入: pandas

import pandas as pd
from parsimonious.grammar import Grammar
from parsimonious.nodes import NodeVisitor

file = """
1: frack 0.733, shale 0.700,
10: space 0.645, station 0.327, nasa 0.258,
4: celebr 0.262, bahar 0.345
"""

grammar = Grammar(
    r"""
    expr    = (garbage / line)+

    line    = id colon pair*
    pair    = term ws weight sep? ws?
    garbage = ws+

    id      = ~"\d+"
    colon   = ws? ":" ws?
    sep     = ws? "," ws?

    term    = ~"[a-zA-Z]+"
    weight  = ~"\d+(?:\.\d+)?"

    ws      = ~"\s+"
    """
)

tree = grammar.parse(file)

class PandasVisitor(NodeVisitor):
    def generic_visit(self, node, visited_children):
        return visited_children or node

    def visit_pair(self, node, visited_children):
        term, _, weight, *_ = visited_children
        return (term.text, weight.text)

    def visit_line(self, node, visited_children):
        id, _, pairs = visited_children
        return [(id.text, *pair) for pair in pairs]

    def visit_garbage(self, node, visited_children):
        return None

    def visit_expr(self, node, visited_children):
        return [item
                for lst in visited_children
                for sublst in lst if sublst
                for item in sublst]

pv = PandasVisitor()
out = pv.visit(tree)

df = pd.DataFrame(out, columns=["Id", "Term", "weight"])
print(df)

这就产生了

   Id     Term weight
0   1    frack  0.733
1   1    shale  0.700
2  10    space  0.645
3  10  station  0.327
4  10     nasa  0.258
5   4   celebr  0.262
6   4    bahar  0.345

在这里, 我们正在构建一个语法与可能的信息: 行或空白。line它是由一个 id (例如 1 ), 后面是冒号 : ()、空格和 pair term weight s, 然后是一个 sep 器。

之后, 我们需要一个 NodeVisitor 类, 以实际做某事, 用检索到的。

第5个答案

下面是你的问题的另一个看法。创建一个列表, 其中将包含每个 id 和术语的列表。然后生成数据框。

import pandas as pd
file=r"give_your_path".replace('\\', '/')
my_list_of_lists=[]#creating an empty list which will contain lists of [Id Term  Weight]
with open(file,"r+") as f:
    for line in f.readlines():#looping every line
        my_id=[line.split(":")[0]]#storing the Id in order to use it in every term
        for term in [s.strip().split(" ") for s in line[line.find(":")+1:].split(",")[:-1]]:
            my_list_of_lists.append(my_id+term)
df=pd.DataFrame.from_records(my_list_of_lists)#turning the lists to dataframe
df.columns=["Id","Term","weight"]#giving columns their names
第6个答案

它是可以只使用完全熊猫:

df = pd.read_csv(StringIO(u"""1: frack 0.733, shale 0.700,
10: space 0.645, station 0.327, nasa 0.258,
4: celebr 0.262, bahar 0.345 """), sep=":", header=None)

#df:
    0                                          1
0   1                 frack 0.733, shale 0.700,
1  10   space 0.645, station 0.327, nasa 0.258,
2   4                 celebr 0.262, bahar 0.345

将列 1 转换为列表, 然后展开:

df[1] = df[1].str.split(",", expand=False)

dfs = []
for idx, rows in df.iterrows():
    print(rows)
    dfslice = pd.DataFrame({"Id": [rows[0]]*len(rows[1]), "terms": rows[1]})
    dfs.append(dfslice)
newdf = pd.concat(dfs, ignore_index=True)

# this creates newdf:
   Id           terms
0   1     frack 0.733
1   1     shale 0.700
2   1
3  10     space 0.645
4  10   station 0.327
5  10      nasa 0.258
6  10
7   4    celebr 0.262
8   4    bahar 0.345

现在, 我们需要将最后一行拆分并删除空:

newdf["terms"] = newdf["terms"].str.strip()
newdf = newdf.join(newdf["terms"].str.split(" ", expand=True))
newdf.columns = ["Id", "terms", "Term", "Weights"]
newdf = newdf.drop("terms", axis=1).dropna()

产生的新 df:

   Id     Term Weights
0   1    frack   0.733
1   1    shale   0.700
3  10    space   0.645
4  10  station   0.327
5  10     nasa   0.258
7   4   celebr   0.262
8   4    bahar   0.345
第7个答案

我可以假设在 "期限" 之前只有 1 个空格吗?

df=pd.DataFrame(columns=['ID','Term','Weight'])
with open('C:/random/d1','r') as readObject:
    for line in readObject:
        line=line.rstrip('\n')
        tempList1=line.split(':')
        tempList2=tempList1[1]
        tempList2=tempList2.rstrip(',')
        tempList2=tempList2.split(',')
        for item in tempList2:
            e=item.split(' ')
            tempRow=[tempList1[0], e[0],e[1]]
            df.loc[len(df)]=tempRow
print(df)
第8个答案
  1. 您可以逐行读取。

  2. 然后, 您可以将 ":" 用于索引, ",", "值" 分开

3.

with open('path/filename.txt','r') as filename:
   content = filename.readlines()
  1. 内容 = [x 的 x 的 x 的. x。

这将为您提供以下结果:

content =[
    ['1','frack 0.733, shale 0.700,'],
    ['10', 'space 0.645, station 0.327, nasa 0.258,'],
    ['4','celebr 0.262, bahar 0.345 ']]

相关问题

通过随机抽样其他列数据创建新列 如何使用熊猫获得包括每一个组合的计数 如何有效地展开矩阵的值与小块? 如何破坏 Python 对象并释放内存 将具有特殊格式的文本文档转换为熊猫数据框架 用两本字典绘制一只熊猫专栏图