Vin*_*ent 170 python numpy dataframe pandas
我有pandas dataframe一列文本字符串包含逗号分隔值.我想拆分每个CSV字段并为每个条目创建一个新行(假设CSV是干净的,只需要在','上拆分).例如,a应该成为b:
In [7]: a
Out[7]: 
    var1  var2
0  a,b,c     1
1  d,e,f     2
In [8]: b
Out[8]: 
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2
到目前为止,我已经尝试了各种简单的函数,但是.apply当在轴上使用时,该方法似乎只接受一行作为返回值,而我无法.transform工作.我们欢迎所有的建议!
示例数据:
from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
               {'var1': 'b', 'var2': 1},
               {'var1': 'c', 'var2': 1},
               {'var1': 'd', 'var2': 2},
               {'var1': 'e', 'var2': 2},
               {'var1': 'f', 'var2': 2}])
我知道这不起作用,因为我们通过numpy丢失DataFrame元数据,但它应该让你了解我尝试做的事情:
def fun(row):
    letters = row['var1']
    letters = letters.split(',')
    out = np.array([row] * len(letters))
    out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)
Max*_*axU 129
UPDATE2:更通用的矢量化函数,适用于normal多list列和多列
def explode(df, lst_cols, fill_value='', preserve_index=False):
    # make sure `lst_cols` is list-alike
    if (lst_cols is not None
        and len(lst_cols) > 0
        and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)
    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()
    # preserve original index values    
    idx = np.repeat(df.index.values, lens)
    # create "exploded" DF
    res = (pd.DataFrame({
                col:np.repeat(df[col].values, lens)
                for col in idx_cols},
                index=idx)
             .assign(**{col:np.concatenate(df.loc[lens>0, col].values)
                            for col in lst_cols}))
    # append those rows that have empty lists
    if (lens == 0).any():
        # at least one list in cells is empty
        res = (res.append(df.loc[lens==0, idx_cols], sort=False)
                  .fillna(fill_value))
    # revert the original index order
    res = res.sort_index()
    # reset index if requested
    if not preserve_index:        
        res = res.reset_index(drop=True)
    return res
演示:
多list列 - 所有list列必须在每行中具有相同的元素数:
In [134]: df
Out[134]:
   aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2         []            []
2   12     3     [1, 2]      [cc, dd]
3   13     4         []            []
In [135]: explode(df, ['num','text'], fill_value='')
Out[135]:
   aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2
4   12     3   1   cc
5   12     3   2   dd
6   13     4
保留原始索引值:
In [136]: explode(df, ['num','text'], fill_value='', preserve_index=True)
Out[136]:
   aaa  myid num text
0   10     1   1   aa
0   10     1   2   bb
0   10     1   3   cc
1   11     2
2   12     3   1   cc
2   12     3   2   dd
3   13     4
建立:
df = pd.DataFrame({
 'aaa': {0: 10, 1: 11, 2: 12, 3: 13},
 'myid': {0: 1, 1: 2, 2: 3, 3: 4},
 'num': {0: [1, 2, 3], 1: [], 2: [1, 2], 3: []},
 'text': {0: ['aa', 'bb', 'cc'], 1: [], 2: ['cc', 'dd'], 3: []}
})
CSV列:
In [46]: df
Out[46]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ
In [47]: explode(df.assign(var1=df.var1.str.split(',')), 'var1')
Out[47]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ
使用这个小技巧,我们可以将类似CSV的列转换为list列:
In [48]: df.assign(var1=df.var1.str.split(','))
Out[48]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ
更新: 通用矢量化方法(也适用于多列):
原DF:
In [177]: df
Out[177]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ
解:
首先让我们将CSV字符串转换为列表:
In [178]: lst_col = 'var1' 
In [179]: x = df.assign(**{lst_col:df[lst_col].str.split(',')})
In [180]: x
Out[180]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ
现在我们可以这样做:
In [181]: pd.DataFrame({
     ...:     col:np.repeat(x[col].values, x[lst_col].str.len())
     ...:     for col in x.columns.difference([lst_col])
     ...: }).assign(**{lst_col:np.concatenate(x[lst_col].values)})[x.columns.tolist()]
     ...:
Out[181]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ
老答案:
受@AFinkelstein解决方案的启发,我想让它更加通用化,可以应用于具有两列以上的DF,并且速度快,几乎和AFinkelstein的解决方案一样快:
In [2]: df = pd.DataFrame(
   ...:    [{'var1': 'a,b,c', 'var2': 1, 'var3': 'XX'},
   ...:     {'var1': 'd,e,f,x,y', 'var2': 2, 'var3': 'ZZ'}]
   ...: )
In [3]: df
Out[3]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ
In [4]: (df.set_index(df.columns.drop('var1',1).tolist())
   ...:    .var1.str.split(',', expand=True)
   ...:    .stack()
   ...:    .reset_index()
   ...:    .rename(columns={0:'var1'})
   ...:    .loc[:, df.columns]
   ...: )
Out[4]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ
DMu*_*gan 100
经过痛苦的实验,找到比接受的答案更快的东西,我得到了这个工作.它在我试用的数据集上运行速度快了大约100倍.
如果有人知道如何使这更优雅,请务必修改我的代码.我找不到一种方法可以在不设置你想要保留的其他列作为索引,然后重置索引并重新命名列,但我想有其他的东西可行.
b = DataFrame(a.var1.str.split(',').tolist(), index=a.var2).stack()
b = b.reset_index()[[0, 'var2']] # var1 variable is currently labeled 0
b.columns = ['var1', 'var2'] # renaming var1
Cha*_*She 68
这样的事情怎么样:
In [55]: pd.concat([Series(row['var2'], row['var1'].split(','))              
                    for _, row in a.iterrows()]).reset_index()
Out[55]: 
  index  0
0     a  1
1     b  1
2     c  1
3     d  2
4     e  2
5     f  2
然后你只需要重命名列
Dan*_*ein 39
这是我为这个常见任务编写的函数.它比Series/ stack方法更有效.列顺序和名称将保留.
def tidy_split(df, column, sep='|', keep=False):
    """
    Split the values of a column and expand so the new DataFrame has one split
    value per row. Filters rows where the column is missing.
    Params
    ------
    df : pandas.DataFrame
        dataframe with the column to split and expand
    column : str
        the column to split and expand
    sep : str
        the string used to split the column's values
    keep : bool
        whether to retain the presplit value as it's own row
    Returns
    -------
    pandas.DataFrame
        Returns a dataframe with the same columns as `df`.
    """
    indexes = list()
    new_values = list()
    df = df.dropna(subset=[column])
    for i, presplit in enumerate(df[column].astype(str)):
        values = presplit.split(sep)
        if keep and len(values) > 1:
            indexes.append(i)
            new_values.append(presplit)
        for value in values:
            indexes.append(i)
            new_values.append(value)
    new_df = df.iloc[indexes, :].copy()
    new_df[column] = new_values
    return new_df
使用此功能,原始问题很简单:
tidy_split(a, 'var1', sep=',')
Nag*_*ran 15
有可能在不改变数据帧结构的情况下拆分和分解数据帧
拆分和扩展特定列的数据
输入:
    var1    var2
0   a,b,c   1
1   d,e,f   2
#Get the indexes which are repetative with the split 
df['var1'] = df['var1'].str.split(',')
df = df.explode('var1')
出去:
    var1    var2
0   a   1
0   b   1
0   c   1
1   d   2
1   e   2
1   f   2
编辑-1
拆分和扩展多列的行
Filename    RGB                                             RGB_type
0   A   [[0, 1650, 6, 39], [0, 1691, 1, 59], [50, 1402...   [r, g, b]
1   B   [[0, 1423, 16, 38], [0, 1445, 16, 46], [0, 141...   [r, g, b]
根据引用列重新索引,将列值信息与堆栈对齐
df = df.reindex(df.index.repeat(df['RGB_type'].apply(len)))
df = df.groupby('Filename').apply(lambda x:x.apply(lambda y: pd.Series(y.iloc[0])))
df.reset_index(drop=True).ffill()
出去:
                Filename    RGB_type    Top 1 colour    Top 1 frequency Top 2 colour    Top 2 frequency
    Filename                            
 A  0       A   r   0   1650    6   39
    1       A   g   0   1691    1   59
    2       A   b   50  1402    49  187
 B  0       B   r   0   1423    16  38
    1       B   g   0   1445    16  46
    2       B   b   0   1419    16  39
cs9*_*s95 15
系列和数据帧的方法定义一个.explode()方法爆炸列出在不同的行。请参阅爆炸类似列表的docs部分。
由于您有一个用逗号分隔的字符串列表,因此请在逗号上分割字符串以获取元素列表,然后explode在该列上调用。
df = pd.DataFrame({'var1': ['a,b,c', 'd,e,f'], 'var2': [1, 2]})
df
    var1  var2
0  a,b,c     1
1  d,e,f     2
df.assign(var1=df['var1'].str.split(',')).explode('var1')
  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2
请注意,explode仅适用于单列(目前)。
NaN和空列表将获得应有的待遇,而您无需花钱就可以解决问题。
df = pd.DataFrame({'var1': ['d,e,f', '', np.nan], 'var2': [1, 2, 3]})
df
    var1  var2
0  d,e,f     1
1            2
2    NaN     3
df['var1'].str.split(',')
0    [d, e, f]
1           []
2          NaN
df.assign(var1=df['var1'].str.split(',')).explode('var1')
  var1  var2
0    d     1
0    e     1
0    f     1
1          2  # empty list entry becomes empty string after exploding 
2  NaN     3  # NaN left un-touched
与基于ravel+ repeat的解决方案(完全忽略空列表并阻塞NaN)相比,这是一个重大优势。
ino*_*odb 14
类似的问题:pandas:如何将列中的文本拆分成多行?
你可以这样做:
>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
   var2 var1
0     1    a
0     1    b
0     1    c
1     2    d
1     2    e
1     2    f
piR*_*red 12
import pandas as pd
import numpy as np
def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})
def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})
explode_str(a, 'var1', ',')
  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2
让我们创建一个d具有列表的新数据框
d = a.assign(var1=lambda d: d.var1.str.split(','))
explode_list(d, 'var1')
  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2
我将使用np.arangewith repeat生成可用于的数据框索引位置iloc。
loc?因为索引可能不是唯一的,所以using loc将返回与查询的索引匹配的每一行。
values属性并对它进行切片?调用时values,如果数据帧的整体位于一个内聚的“块”中,则Pandas将返回作为“块”的数组的视图。否则,熊猫将不得不拼凑出一个新的阵列。排序时,该数组必须具有统一的dtype。通常,这意味着返回dtype为的数组object。通过使用iloc而不是切片values属性,我减轻了自己的负担。
assign?当我使用assign使用相同的列名说我炸响,我覆盖现有的列并保持其在数据帧的位置。
通过iloc在重复位置上使用,所得索引显示了相同的重复模式。对列表或字符串的每个元素重复一次。
可以使用reset_index(drop=True)
我不想过早地拆分字符串。因此,我sep假设如果要拆分,则计算该参数的出现次数,结果列表的长度将比分隔符的数量多一。
然后,我将其sep用于join字符串split。
def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})
与字符串相似,不同的是我不需要计数出现的次数,sep因为它已经分裂了。
我用Numpy concatenate将清单加在一起。
import pandas as pd
import numpy as np
def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})
我想出了一个具有任意列数的数据帧的解决方案(同时仍然只分离一列的条目).
def splitDataFrameList(df,target_column,separator):
    ''' df = dataframe to split,
    target_column = the column containing the values to split
    separator = the symbol used to perform the split
    returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
    The values in the other columns are duplicated across the newly divided rows.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df
| 归档时间: | 
 | 
| 查看次数: | 93530 次 | 
| 最近记录: |