2016-04-14 77 views
2

有缺失数据的处理我有一个数据集,如:在一列文本文件

IM,XX 
IS,YY 
SG,3 
OTPL,90 
TTPL,90 
IM,AA 
IS,BB 
SG,3 
TTPL,50 
IM,ZZ 
IS,CC 
OTPL,10 

每一行包含key,value对,我需要将其转换成表格的形式,以便进行一些分析。变量IM表示行的索引,下面的参数是列。对我来说,棘手的部分是考虑到可能缺失的值。预期的结果是:

IM IS OTPL SG TTPL 
XX YY 90  3 90  
AA BB null  3 50  
ZZ CC 10 null null 

“注意空值”。

我有一个解决方案,但没有那么高效,当数据集非常大时,它不是一个合适的方法。我用下面的策略:

  1. 随着awk,添加一个额外的指数每个寄存器(行)。它创建了一个计数器n当出现IM增加它:

    $ awk -F, 'BEGIN{n = 0}{ if($1 == "IM"){n += 1} print n","$0}' inputdata.txt 
    1,IM,XX 
    1,IS,YY 
    1,SG,3 
    1,OTPL,90 
    1,TTPL,90 
    2,IM,AA 
    2,IS,BB 
    2,SG,3 
    2,TTPL,50 
    3,IM,ZZ 
    3,IS,CC 
    3,OTPL,10 
    
  2. 接下来,读取使用pandas,应用由上述指标groupby先前的结果,并创建应用concatpivot子表的新表:

    In[1]:import pandas as pd 
         gb = pd.read_csv("outdata.txt", names = ["id","key","value"]).groupby("id") 
         res = pd.concat([df.pivot(index="id", columns='key', values='value') for g, df in gb]) 
         res 
    Out[1]: 
        IM IS OTPL SG TTPL 
    id      
    1 XX YY 90 3 90 
    2 AA BB NaN 3 50 
    3 ZZ CC 10 NaN NaN 
    

最后一步非常昂贵。

有没有人有类似的问题?只有用命令行才能解决这个问题。

提前致谢!

+1

所以你的目标是做仅使用shell命令对这些数据的分析(即,“只使用命令行”)?我想这并不清楚为什么你的'熊猫'解决方案对你来说不够好。你的数据集有多大? – dbliss

+0

我假设数据的顺序非常重要,并且顺序的改变可能会破坏结果。 – Alexander

+0

我认为这必须是使用shell命令或其他工具的更高效方式。当我如上所述使用'pandas'时,这个操作需要大约10秒钟的时间,只有1000行的数据集(在普通的台式计算机中)也消耗大量内存。 –

回答

2

[更新]纯GAWK解决方案:

BEGIN { 
    FS=OFS="," 
    n = 0 
} 
{ 
    if($1 == "IM") { 
     n++ 
    } 
    keys[$1]++ 
    vals[n,$1]=$2 
} 
END { 
    l=asorti(keys, copy) 
    printf "id" 
    for (i=1; i<=l; i++) { 
     printf "%s%s", FS, copy[i] 
    } 
    print "" 

    for (i=1; i<=n; i++) { 
     printf "%s", i 
     for (k=1; k<=l; k++) { 
      printf "%s%s", FS, vals[i,copy[k]] 
     } 
     print "" 
    } 
} 

输出:

{ .data } » awk -f prg.awk data.csv 
id,IM,IS,OTPL,SG,TTPL 
1,XX,YY,90,3,90 
2,AA,BB,,3,50 
3,ZZ,CC,10,, 

[老]熊猫的解决方案:

我想你可以只使用pivot_table()代替groupby() + concat()

In [105]: df 
Out[105]: 
    id key val 
0 1 IM XX 
1 1 IS YY 
2 1 SG 3 
3 1 OTPL 90 
4 1 TTPL 90 
5 2 IM AA 
6 2 IS BB 
7 2 SG 3 
8 2 TTPL 50 
9 3 IM ZZ 
10 3 IS CC 
11 3 OTPL 10 

In [106]: df.pivot_table(index='id', columns='key', values='val', aggfunc='sum', fill_value=np.nan) 
Out[106]: 
key IM IS OTPL SG TTPL 
id 
1 XX YY 90 3 90 
2 AA BB NaN 3 50 
3 ZZ CC 10 NaN NaN 

pivot()如果你没有重复的(如在您的样本数据集):

In [109]: df.pivot(index='id', columns='key', values='val') 
Out[109]: 
key IM IS OTPL SG TTPL 
id 
1 XX YY 90  3 90 
2 AA BB None  3 50 
3 ZZ CC 10 None None 

NaN!而非None S也是一样的:

In [110]: df.pivot(index='id', columns='key', values='val').fillna(np.nan) 
Out[110]: 
key IM IS OTPL SG TTPL 
id 
1 XX YY 90 3 90 
2 AA BB NaN 3 50 
3 ZZ CC 10 NaN NaN 
+0

优秀的解决方案。它大大缩短了执行时间! :-)。在这种情况下'aggfunc'不是必需的,因为每个'id'都有唯一的'key'。非常感谢! –

+0

@ej_f,总是很乐意帮忙。我已经添加了一个AWK解决方案 - 请你检查一下,并给出一个简短的反馈 - 哪一个更快? – MaxU

+1

@ej_f如果上述答案解决了您的问题(看起来是这种情况),请务必单击答案旁边的复选标记,将问题标记为已解决。 – beroe

1
def my_transform(infile, outfile): 
    df = pd.read_csv(infile, header=None, sep=",", names=['id', None]) 
    df = df.groupby([(df.id == 'IM').cumsum(), 'id']).first().unstack() 
    df.columns = df.columns.droplevel() 
    df.to_csv(outfile, index=None) 

infile = "..." 
outfile = "..." 
my_transform(infile, outfile) 

>>> !cat "..." # outfile 
IM,IS,OTPL,SG,TTPL 
XX,YY,90,3,90 
AA,BB,,3,50 
ZZ,CC,10,, 

groupby的关键是在(df.id == 'IM').cumsum()上分组,这意味着发生第一列中的'即时消息'的描述描绘了一个新的群体。 my_transform函数获取输入文件,将其转换为所需的输出,然后将结果保存回文件。

df['group'] = (df.id == 'IM').cumsum() 
>>> df 
     id NaN group 
0  IM XX  0 
1  IS YY  0 
2  SG 3  0 
3 OTPL 90  0 
4 TTPL 90  0 
5  IM AA  1 
6  IS BB  1 
7  SG 3  1 
8 TTPL 50  1 
9  IM ZZ  2 
10 IS CC  2 
11 OTPL 10  2 
+0

谢谢@亚历山大,绝对是一位熊猫大师!你的想法真棒!下面的文章显示了解决方案的执行时间。 –

1

非常感谢@Alexander和@MaxU的评论。

awk纯溶液的性能略好于pandas。 以下结果用35500行的数据集获得的:

# initial solution (pandas + awk) 
In [2]: %timeit ej_f_pandas() 
1 loops, best of 3: 1min 14s per loop 

# maxu's solution (pandas + awk) 
In [3]: %timeit maxu_pandas() 
1 loops, best of 3: 697 ms per loop 

# alexander's solution (pandas) 
In [4]: %timeit alexander_pandas() 
1 loops, best of 3: 518 ms per loop 

# maxu's solution (awk) 
In [5]: %timeit maxu_awk() 
1 loops, best of 3: 499 ms per loop 
+2

谢谢比较 - 比较不同的解决方案/方法总是很有趣 – MaxU