处理一列文本文件中的缺失数据
Dealing with missing data in an one column text file
我的数据集如下:
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
"note the null values".
我有一个解决方案,但效率不高,当数据集很大时,这不是一个合适的方法。我使用以下策略:
与awk
一起,为每个寄存器(行)添加一个额外的索引。它创建一个计数器 n
并在出现 IM
时递增它:
$ awk -F, 'BEGIN{n = 0}{ if( == "IM"){n += 1} print n","[=12=]}' 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
接下来,使用 pandas
读取先前的结果,通过上述索引应用 groupby
并创建一个新的 table 应用 concat
到 pivot
subtables:
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
最后一步非常昂贵。
有没有人遇到过类似的问题?最好只用命令行解决这个问题。
提前致谢!
[更新] 纯 GAWK 解决方案:
BEGIN {
FS=OFS=","
n = 0
}
{
if( == "IM") {
n++
}
keys[]++
vals[n,]=
}
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,,
[OLD] Pandas 解决方案:
我认为你可以只使用 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
s 相同而不是 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
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()
上分组,这意味着第一列中 'IM' 的出现划定了一个新组。 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
非常感谢@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
我的数据集如下:
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
"note the null values".
我有一个解决方案,但效率不高,当数据集很大时,这不是一个合适的方法。我使用以下策略:
与
awk
一起,为每个寄存器(行)添加一个额外的索引。它创建一个计数器n
并在出现IM
时递增它:$ awk -F, 'BEGIN{n = 0}{ if( == "IM"){n += 1} print n","[=12=]}' 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
接下来,使用
pandas
读取先前的结果,通过上述索引应用groupby
并创建一个新的 table 应用concat
到pivot
subtables: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
最后一步非常昂贵。
有没有人遇到过类似的问题?最好只用命令行解决这个问题。
提前致谢!
[更新] 纯 GAWK 解决方案:
BEGIN {
FS=OFS=","
n = 0
}
{
if( == "IM") {
n++
}
keys[]++
vals[n,]=
}
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,,
[OLD] Pandas 解决方案:
我认为你可以只使用 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
s 相同而不是 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
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()
上分组,这意味着第一列中 'IM' 的出现划定了一个新组。 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
非常感谢@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