pandas — запись файла с несколькими разделителями. медленная конкатенация строк

#python-3.x #pandas

#python-3.x #pandas

Вопрос:

Мне нужно записать файлы, которые имеют формат с меткой, основанной на серии наборов, разделенных точками, и числовое значение, разделенное пробелом. Некоторые наборы могут быть строками или целыми числами, а значения могут быть целыми числами или числами с плавающей запятой

например:

a.1.1 0.19

a.1.2 1.23

1.5123.29 0

 def write_myfile(df,file):
    cols = df.columns[:-1]
    df2 = pd.DataFrame()
    df2['Labels'] = df[cols].apply(lambda x: '.'.join(x.dropna().astype(str).values.tolist()), axis=1)
    df2['Values'] = df['value']
    df2.to_csv(file,sep = ' ',header=False,index=False)
return dd
  

Итак, на данный момент я использую фрейм данных pandas с метками в первых столбцах и значением в конечном столбце. Это работает для небольших файлов, но невероятно неэффективно. Мне нужно записать файлы примерно в 3,5 миллиона строк.

Есть предложения?

Комментарии:

1. мой текущий метод повышения скорости заключается в записи файла в csv с использованием разделителя «.», затем прочитайте его снова, используя разделитель «,», указывающий dtype как string, затем объедините значения. это намного быстрее, но кажется немного ненадежным

Ответ №1:

Вы можете использовать понимание вложенного списка, потому что его нужно удалить NaN .

Я думаю, у вас есть NaN в значениях, потому что используйте dropna .

Сначала экспортируйте все столбцы без последнего в numpy array by values , а затем в list . Последнее создание нового DataFrame с помощью конструктора:

 cols = df.columns[:-1]
a = pd.Series(['.'.join([str(y) for y in x if pd.notnull(y)])
               for x in df[cols].values.tolist()])
b = df['value']

df = pd.DataFrame({'Labels' : a, 'Values' : b})
print (df)
      Labels  Values
0      a.1.1    0.19
1        1.2    1.23
2      a.1.1    0.19
3        1.2    1.23
4  1.5123.29    0.00
  

Тайминги:

(len(df)=5k) :

 In [280]: %timeit (orig(df))
1 loop, best of 3: 22.2 s per loop

In [281]: %timeit (jez(df1))
10 loops, best of 3: 145 ms per loop

df = pd.DataFrame({
'value': {0: 0.19, 1: 1.23, 2: 0.19, 3: 1.23, 4: 0.0}, 
's': {0: 1, 1: 2, 2: 1, 3: 2, 4: 29}, 
'b': {0: 1, 1: 1, 2: 1, 3: 1, 4: 5123}, 
'a': {0: 'a', 1: np.nan, 2: 'a', 3: np.nan, 4: '1'}})
print (df)

     a     b   s  value
0    a     1   1   0.19
1  NaN     1   2   1.23
2    a     1   1   0.19
3  NaN     1   2   1.23
4    1  5123  29   0.00

df = pd.concat([df]*10000).reset_index(drop=True)

df1 = df.copy()

def orig(df):
    cols = df.columns[:-1]
    df2 = pd.DataFrame()
    df2['Labels'] = df[cols].apply(lambda x: '.'.join(x.dropna().astype(str).values.tolist()), axis=1)
    df2['Values'] = df['value']

    return (df2)


def jez(df): 
    cols = df.columns[:-1]
    a = pd.Series(['.'.join([str(y) for y in x if pd.notnull(y)]) for x in df[cols].values.tolist()])
    b = df['value']
    df = pd.DataFrame({'Labels' : a, 'Values' : b})
    return (df)

print (orig(df))
print (jez(df1))
  

Другое более эффективное решение, но оно зависит от данных, если работает для вас очень хорошо:

Сравните с помощью str(y) != 'nan' вместо pd.notnull(y) :

 In [298]: %timeit (jez1(df1))
10 loops, best of 3: 114 ms per loop

def jez1(df): 
    cols = df.columns[:-1]
    a = pd.Series(['.'.join([str(y) for y in x if str(y) != 'nan']) for x in df[cols].values.tolist()])
    b = df['value']
    df = pd.DataFrame({'Labels' : a, 'Values' : b})
    return (df)
  

Комментарии:

1. потрясающе, это намного быстрее. a = pd.Series([‘.’.join([str(y) для y в x, если pd.notnull(y)]) для x в df[cols].значения. tolist()]) намного быстрее, чем df[cols].apply(лямбда x: ‘.’.join(x.dropna().astype(str).values. tolist()), axis=1) большое спасибо