2016-09-06 17 views
6

我有一个线大CSV文件看起来像如何重新映射ID,以连续的数字迅速

stringa,stringb 
stringb,stringc 
stringd,stringa 

我需要它这样的ID被连续编号为0转换在这种情况下会工作

0,1 
1,2 
3,0 

我当前的代码看起来像:

import csv 
names = {} 
counter = 0 
with open('foo.csv', 'rb') as csvfile: 
    reader = csv.reader(csvfile) 
    for row in reader: 
     if row[0] in names: 
      id1 = row[0] 
     else: 
      names[row[0]] = counter 
      id1 = counter 
      counter += 1 
     if row[1] in names: 
      id2 = row[1] 
     else: 
      names[row[1]] = counter 
      id2 = counter 
      counter += 1 
    print id1, id2 

的Python类型的字典使用大量的内存黯然我的投入很大。

当输入过大的字典,以适应在内存

我也有兴趣,如果有一般的解决这个问题的一个更好/更快的方法我能做些什么。

+0

使用字典/哈希映射的一般策略是正确的,虽然你做得有点笨拙。当你说你的输入太大而不适合记忆时,我们在这里说什么?如果没有查询表(字典)或其他权威性参考,您将无法保证唯一性或连贯性。 –

+0

@NathanielFord我很想知道一个不太笨拙的开始。 – eleanora

+0

当你连续地从你给出的例子中说你想要一个输出0,1,2,3等? –

回答

6
df = pd.DataFrame([['a', 'b'], ['b', 'c'], ['d', 'a']]) 

v = df.stack().unique() 
v.sort() 
f = pd.factorize(v) 
m = pd.Series(f[0], f[1]) 

df.stack().map(m).unstack() 

enter image description here

+1

这是一个聪明,非常整洁的解决方案! – MaxU

+1

谢谢@MaxU – piRSquared

+2

我也尝试将它分类以节省内存... – MaxU

2

UPDATE:这里是一个存储器中保存的解决方案,其中所有的字符串转换为数值类:

In [13]: df 
Out[13]: 
     c1  c2 
0 stringa stringb 
1 stringb stringc 
2 stringd stringa 
3 stringa stringb 
4 stringb stringc 
5 stringd stringa 
6 stringa stringb 
7 stringb stringc 
8 stringd stringa 

In [14]: x = (df.stack() 
    ....:  .astype('category') 
    ....:  .cat.rename_categories(np.arange(len(df.stack().unique()))) 
    ....:  .unstack()) 

In [15]: x 
Out[15]: 
    c1 c2 
0 0 1 
1 1 2 
2 3 0 
3 0 1 
4 1 2 
5 3 0 
6 0 1 
7 1 2 
8 3 0 

In [16]: x.dtypes 
Out[16]: 
c1 category 
c2 category 
dtype: object 

OLD答案:

我觉得你可以分类你的列:

In [63]: big.head(15) 
Out[63]: 
     c1  c2 
0 stringa stringb 
1 stringb stringc 
2 stringd stringa 
3 stringa stringb 
4 stringb stringc 
5 stringd stringa 
6 stringa stringb 
7 stringb stringc 
8 stringd stringa 
9 stringa stringb 
10 stringb stringc 
11 stringd stringa 
12 stringa stringb 
13 stringb stringc 
14 stringd stringa 

In [64]: big.info() 
<class 'pandas.core.frame.DataFrame'> 
RangeIndex: 30000000 entries, 0 to 29999999 
Data columns (total 2 columns): 
c1 object 
c2 object 
dtypes: object(2) 
memory usage: 457.8+ MB 

所以big DF有30M行,它的大小为i s约。 460MiB ...

让我们对其进行分类:

In [65]: cat = big.apply(lambda x: x.astype('category')) 

In [66]: cat.info() 
<class 'pandas.core.frame.DataFrame'> 
RangeIndex: 30000000 entries, 0 to 29999999 
Data columns (total 2 columns): 
c1 category 
c2 category 
dtypes: category(2) 
memory usage: 57.2 MB 

现在只需要57MiB和长得一模一样:

In [69]: cat.head(15) 
Out[69]: 
     c1  c2 
0 stringa stringb 
1 stringb stringc 
2 stringd stringa 
3 stringa stringb 
4 stringb stringc 
5 stringd stringa 
6 stringa stringb 
7 stringb stringc 
8 stringd stringa 
9 stringa stringb 
10 stringb stringc 
11 stringd stringa 
12 stringa stringb 
13 stringb stringc 
14 stringd stringa 

让我们比较它的大小用类似的数字DF:

​​
+0

这个代码可以用新的数字ID输出吗? – eleanora

+0

我的意思是,我真的需要输出格式与输入相同,但重命名为id。 – eleanora

+0

@eleanora,请检查piRSquared的解决方案 - 它会给你ID – MaxU

3

如果你想要一个id数组,你可以使用factorize

df = pd.read_csv(data, header=None, prefix='Col_') 
print (pd.factorize(np.hstack(df.values))) 

(array([0, 1, 1, 2, 3, 0]), array(['stringa', 'stringb', 'stringc', 'stringd'], dtype=object)) 

编辑:(按照评语)

您可以采取factorize方法之后获得的元组的切片和通过替换彼此如图相应映射到整个dataframe

num, letter = pd.factorize(np.hstack(df.values)) 

df.replace(to_replace=sorted(list(set(letter))), value=sorted(list(set(num)))) 

    Col_0 Col_1 
0  0  1 
1  1  2 
2  3  0 
+0

我真的需要输出格式与输入相同,只需重命名。 – eleanora