使用分类数据计数创建pandas DataFrame

时间:2015-11-06 22:31:33

标签: python excel pandas categorical-data

我有一堆调查数据按每个问题的每个选择的答案数量(多项选择题)进行细分。我有几个不同的课程,学期,部分等的每个摘要之一。不幸的是,我的所有数据都是在PDF打印输出给我,我无法获得数字数据。从好的方面来说,这意味着我可以自由统治格式化我的数据文件,但是我需要这样才能将它导入到Pandas中。

如何将我的数据导入Pandas,最好不需要逐行复制(我的摘要代表的每个条目都有一行)。

数据

我的调查包括几个多项选择题。我有多个为每个问题选择每个选项的受访者。类似的东西:

Course Number: 100
Semester: Spring
Section: 01

Question 1
----------
Option A: 27
Option B: 30
Option C: 0
Option D: 2

Question 2
----------
Option X: 20
Option Y: 10

如果我的数据已经在Pandas中,那么基本上我有.value_counts()个结果。请注意,问题并不总是具有相同数量的选项(类别),并且它们并不总是具有相同数量的响应者。对于多个课程编号,学期和部分,我会得到类似的结果。

类别ABC等只是占位符,用于表示实际数据中每个响应类别的标签。

另外,我必须手动将所有这些输入到某些内容中,所以我并不担心阅读上面的特定文件格式,它只是代表我在我面前的实际打印输出上的内容。

目标

我想通过告诉Pandas在每个问题的每个响应类别中有多少来重新创建Pandas中的响应数据。基本上我想要一个看起来像上面的响应数据的Excel文件或CSV,以及一个看起来像的Pandas DataFrame:

Course Number   Semester   Section   Q1   Q2
100             Spring     01        A    X
100             Spring     01        A    X
... (20 identical entries)
100             Spring     01        A    Y
100             Spring     01        A    Y
... (7 of these)
100             Spring     01        B    Y
100             Spring     01        B    Y
100             Spring     01        B    Y
100             Spring     01        B    N/A  (out of Q2 responses)
...
100             Spring     01        D    N/A
100             Spring     01        D    N/A

我应该注意,我没有在这里复制实际的响应数据,因为我无法知道为问题1选择选项D的人也没有选择选项X代表问题2.我只想让每个结果的数量显示相同,而我的df.count_values()输出基本上可以告诉我我的摘要已经说过的内容。

到目前为止的尝试

到目前为止,我能想到的最好的方法是将每个响应作为自己的行复制到excel文件中,然后导入该文件并转换为类别:

import pandas as pd

df = pd.read_excel("filename")
df["Q1"] = df["Q1"].astype("category")
df["Q2"] = df["Q2"].astype("category")

这有几个问题。首先,我有成千上万的响应,所以创建所有这些行将花费太长时间。我更喜欢紧凑的方法,直接记录每个响应的数量,然后将其导入Pandas。

其次,当我对每个问题没有相同数量的回答时,这会变得有点尴尬。首先,为了节省输入每个响应的时间,我只是在该值与上一行不同时在列中放置一个值,然后使用.ffill()来转发填充Pandas DataFrame中的值。问题在于所有NaN值都已填满,因此对于不同的问题我不能有不同数量的回复。

我与第一次在Excel中记录数据的想法没有结合,所以如果有更简单的方式使用别的东西,我都是耳朵。

如果还有其他方式来看待这个比我在这里尝试更有意义的问题,我也愿意听到这个问题。

编辑:有点工作

我稍微切换了一个齿轮并制作了一个Excel文件,其中每个工作表都是一个调查摘要,前几列确定了CourseSemesterSection,{{1}等等,然后我有一列可能的Year类别。文件的其余部分包含每个问题的列,然后是与匹配该问题的响应相对应的每行中的响应数。然后我导入每个工作表并连接:

Response

这似乎有效,但我最终得到了一张非常丑陋的表格(对于所有与每个问题实际上并不对应的回复,都有很多NaN)。我可以通过以下方式绘制任何一个问题的结果:

df = [pd.read_excel("filename", sheetname=i, index_col=range(0,7)) for i in range(1,3)]
df = pd.concat(df)

我觉得必须有更好的方法来做到这一点,可能有多个索引或某种3D数据结构......

1 个答案:

答案 0 :(得分:0)

获取信息的一种愚蠢方法是首先拆分-----然后使用正则表达式。

对于每门课程,如下所示:

In [11]: s
Out[11]: 'Semester: Spring\nSection: 01\nQuestion 1\n----------\nOption A: 27\nOption B: 30\nOption C: 0\nOption D: 2\n\nQuestion 2\n----------\nOption A: 20\nOption B: 10'

In [12]: blocks = s.split("----------")

解析第一个块中的信息,使用正则表达式或仅拆分:

In [13]: semester = re.match("Semester: (.*)", blocks[0]).groups()[0]

In [14]: semester
Out[14]: 'Spring'

解析每个块的选项信息:

def parse_block(lines):
    d = {}
    for line in lines:
        m = re.match("Option ([^:]+): (\d+)", line)
        if m:
            d[m.groups()[0]] = int(m.groups()[1])
    return d

In [21]: [parse_block(x.splitlines()) for x in blocks[1:]]
Out[21]: [{'A': 27, 'B': 30, 'C': 0, 'D': 2}, {'A': 20, 'B': 10}]

您可以同样提出问题编号(如果您不知道他们是连续的):

In [22]: questions = [int(re.match(".*Question (\d+)", x, re.DOTALL).groups()[0]) for x in blocks[:-1]]

In [23]: questions
Out[23]: [1, 2]

并将它们压缩在一起:

In [31]: dict(zip(questions, ds))
Out[31]: {1: {'A': 27, 'B': 30, 'C': 0, 'D': 2}, 2: {'A': 20, 'B': 10}}

In [32]: pd.DataFrame(dict(zip(questions, ds)))
Out[32]:
    1   2
A  27  20
B  30  10
C   0 NaN
D   2 NaN

我把它们放在(课程,学期,部分)的另一个词典中 - > DataFrame然后连接并找出从大型MultiIndex dataFrame到哪里去的地方...