使用分类数据计数创buildpandas数据框

我有一堆调查数据按照每个问题的答案数量(多选题)分解。 我有几个不同的课程,学期,部分等的每一个摘要之一。不幸的是,我所有的数据是在PDF打印输出给我,我不能得到数字数据。 在光明的一面,这意味着我有自由统治格式化我的数据文件,但我需要,以便我可以导入到pandas。

如何将数据导入到pandas中,最好不需要逐行复制(每个条目由我的摘要表示)。

数据

我的调查包括几个select题。 对于每个问题,我有多less个答复者select了每个选项。 就像是:

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 

所以基本上我有.value_counts()结果,如果我的数据已经在pandas。 请注意,问题并不总是具有相同数量的选项(类别),并不总是具有相同数量的答复者。 我将有多个课程编号,学期和部分类似的结果。

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

另外,我不得不手动input所有的东西,所以我不担心读取上面的具体文件格式,它只是代表我在我面前的实际打印输出。

目标

我想通过告诉Pandas每个问题的每个回答类别有多less来重新创buildPandas中的回答数据。 基本上我想要一个Excel文件或CSV,看起来像上面的响应数据,和一个pandasDataFrame,看起来像:

 Course Number Semester Section Q1 Q2 100 Spring 01 AX 100 Spring 01 AX ... (20 identical entries) 100 Spring 01 AY 100 Spring 01 AY ... (7 of these) 100 Spring 01 BY 100 Spring 01 BY 100 Spring 01 BY 100 Spring 01 BN/A (out of Q2 responses) ... 100 Spring 01 DN/A 100 Spring 01 DN/A 

我应该注意到,我没有在这里复制实际的响应数据,因为我无法知道select问题1的选项D的人没有为问题2select选项X我只是想要每个结果的数目显示相同,并为我的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") 

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

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

我不想结婚的想法,首先在Excel中logging数据,所以如果有一个更简单的方法使用别的东西我都耳朵。

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

编辑:有种工作

我切换了一下档案,并创build了一个Excel文件,其中每张表是一个调查总结,前几列标识CourseSemesterSectionYear等,然后我有一列可能的Response类别。 文件的其余部分包含每个问题的一个列,然后是与匹配该问题的回答对应的每一行中的回复数量。 然后我导入每个工作表并连接:

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

这似乎工作,但我最终得到一个非常丑陋的表(大量NaN的所有答案,实际上并不对应每个问题)。 我可以解决这个问题,用任何一个问题来绘制结果,例如:

 df_grouped = df.groupby("Response", sort=False).aggregate(sum) # group according to response df_grouped["Q1"][np.isfinite(df_grouped["Q1"])].plot(kind="bar") # only plot responses that have values 

我觉得有一个更好的方法来做到这一点,也许有多个指标或某种3D数据结构…

一个奇怪的方式来获取信息,首先是拆分—–,然后使用正则expression式。

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

 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("----------") 

从第一个块parsing出信息,使用正则expression式或者只是分割:

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

从每个块parsing选项信息:

 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,然后concat和工作,从大的MultiIndex数据框去哪里…