将条目重新分配到庞大的pandas数据框中的多个行中

时间:2017-09-04 14:21:27

标签: python pandas numpy dataframe

我有一个数据框(从CSV文件派生),大约有100M条目,如下所示:

df1:

        var1     var2
0          1        2
1          2        1
2          1  {3,4,5}
3          5        6
4  {4,5,6,7}        8

我需要将其转换为一个新的数据框,其中(对于每一行)大括号中的每个元素都需要与另一列中的元素相关联,即

df2:

  var1 var2
0    1    2
1    2    1
2    1    3
3    1    4
4    1    5
5    5    6
6    4    8
7    5    8
8    6    8
9    7    8

每个元素都是一个字符串,甚至是大括号条目本身。请注意,支撑元素可以位于任一列中。有谁知道如何有效地为大约100M条目的数据集实现这一目标?提前致谢。

Python示例:

import pandas as pd

df1 = pd.DataFrame([{'var1': '1', 'var2': '2'},
               {'var1': '2', 'var2': '1'},
               {'var1': '1', 'var2': '{3,4,5}'},
               {'var1': '5', 'var2': '6'},
               {'var1': '{4,5,6,7}', 'var2': '8'}])


df2 = pd.DataFrame([{'var1': '1', 'var2': '2'},
               {'var1': '2', 'var2': '1'},
               {'var1': '1', 'var2': '3'},
               {'var1': '1', 'var2': '4'},
               {'var1': '1', 'var2': '5'},
               {'var1': '5', 'var2': '6'},
               {'var1': '4', 'var2': '8'},
               {'var1': '5', 'var2': '8'},
               {'var1': '6', 'var2': '8'},
               {'var1': '7', 'var2': '8'}])

到目前为止,我已经这样做了,但它很慢并使用了另一个数据框。

# Put row with braces in the second column
def swap_cols(row):
    if '{' in row[0]:
        return (row[1], row[0])
    return row

# Convert the braces into a list
def parse_str(s):
    if '{' in s:
        s = s[1:-1]
        return s.split(',')
    return [s]


df3 = df1.apply(swap_cols, axis=1)

df3.var2 = df3.var2.apply(parse_str)

# Show that it works
for ridx, row in df3.iterrows():
    for ele in row.var2:
        print row.var1, ele

3 个答案:

答案 0 :(得分:2)

您可以np.vstacknp.meshgridreshape使用

sdf = df.apply(lambda x:(x.str.strip('{}').str.split(',')))

def cartesian(x): 
    return np.vstack(np.array([np.array(np.meshgrid(*i)).T.reshape(-1,2) for i in x.values]))

ndf = pd.DataFrame(cartesian(sdf),columns=sdf.columns)

如果你想剥离和拆分然后应用笛卡儿

%%time 
100 loops, best of 3: 4 ms per loop

如果您确实有条带化和分割数据帧,那么:

1000 loops, best of 3: 564 µs per loop

输出:

  var1 var2
0    1    2
1    2    1
2    1    3
3    1    4
4    1    5
5    5    6
6    4    8
7    5    8
8    6    8
9    7    8

答案 1 :(得分:0)

你可以尝试:

# isolate these cases as they will be treated separately
case1 = df1['var1'].str.contains('{')
case2 = df1['var2'].str.contains('}')

# convert to lists
import ast
df1 = df1.apply(lambda col: col.str.replace('{', '[').str.replace('}', ']')) \
         .applymap(ast.literal_eval)

在第二种情况下:

df1[case2].groupby('var1')['var2'].apply(lambda g: pd.Series(g.sum())) \
          .reset_index(-1, drop=True).reset_index()

应用sum将连接var1的每个值的列表(如果有多个),并且转换为pandas.Series将提供您正在寻找的形状。

然后你可以将所有内容连接起来:

pd.concat([
    df1[~case1 & ~case2],
    df1[case1].groupby('var2')['var1'].apply(lambda g: pd.Series(g.sum())).reset_index(-1, drop=True).reset_index(),
    df1[case2].groupby('var1')['var2'].apply(lambda g: pd.Series(g.sum())).reset_index(-1, drop=True).reset_index()
]).sort_values('var1')  # sorting optional

答案 2 :(得分:0)

使用numpy.repeatnumpy.concatenate进行展平:

#create lists by remove {} and split
splitted1 = df1['var1'].str.strip('{}').str.split(',')
#get legths of lists
lens1 = splitted1.str.len()

splitted2 = pd.Series(np.repeat(df1['var2'].values, lens1)).str.strip('{}').str.split(',')
lens2 = splitted2.str.len()

df = pd.DataFrame({'a':np.repeat(np.concatenate(splitted1), lens2),
                   'b':np.concatenate(splitted2)})
print (df)
   a  b
0  1  2
1  2  1
2  1  3
3  1  4
4  1  5
5  5  6
6  4  8
7  5  8
8  6  8
9  7  8