Подтвердить что ты не робот

Python pandas удалить дубликаты столбцов

Каков самый простой способ удаления повторяющихся столбцов из фреймворка данных?

Я читаю текстовый файл, который имеет повторяющиеся столбцы:

import pandas as pd

df=pd.read_table(fname)

Имена столбцов:

Time, Time Relative, N2, Time, Time Relative, H2, etc...

Все столбцы времени и времени содержат одни и те же данные. Я хочу:

Time, Time Relative, N2, H2

Все мои попытки удалить, удалить и т.д., например:

df=df.T.drop_duplicates().T

Результат в однозначно оцененных ошибках индекса:

Reindexing only valid with uniquely valued index objects

Извините за то, что вы Pandas noob. Любые предложения будут оценены.


Дополнительные сведения

Pandas версия: 0.9.0
Python Версия: 2.7.3
Windows 7
(устанавливается через Pythonxy 2.7.3.0)

файл данных (обратите внимание: в реальном файле столбцы разделены вкладками, здесь они разделены 4 пробелами):

Time    Time Relative [s]    N2[%]    Time    Time Relative [s]    H2[ppm]
2/12/2013 9:20:55 AM    6.177    9.99268e+001    2/12/2013 9:20:55 AM    6.177    3.216293e-005    
2/12/2013 9:21:06 AM    17.689    9.99296e+001    2/12/2013 9:21:06 AM    17.689    3.841667e-005    
2/12/2013 9:21:18 AM    29.186    9.992954e+001    2/12/2013 9:21:18 AM    29.186    3.880365e-005    
... etc ...
2/12/2013 2:12:44 PM    17515.269    9.991756+001    2/12/2013 2:12:44 PM    17515.269    2.800279e-005    
2/12/2013 2:12:55 PM    17526.769    9.991754e+001    2/12/2013 2:12:55 PM    17526.769    2.880386e-005
2/12/2013 2:13:07 PM    17538.273    9.991797e+001    2/12/2013 2:13:07 PM    17538.273    3.131447e-005
4b9b3361

Ответ 1

Похоже, вы уже знаете уникальные имена столбцов. Если это произойдет, то df = df['Time', 'Time Relative', 'N2'] будет работать.

Если нет, ваше решение должно работать:

In [101]: vals = np.random.randint(0,20, (4,3))
          vals
Out[101]:
array([[ 3, 13,  0],
       [ 1, 15, 14],
       [14, 19, 14],
       [19,  5,  1]])

In [106]: df = pd.DataFrame(np.hstack([vals, vals]), columns=['Time', 'H1', 'N2', 'Time Relative', 'N2', 'Time'] )
          df
Out[106]:
   Time  H1  N2  Time Relative  N2  Time
0     3  13   0              3  13     0
1     1  15  14              1  15    14
2    14  19  14             14  19    14
3    19   5   1             19   5     1

In [107]: df.T.drop_duplicates().T
Out[107]:
   Time  H1  N2
0     3  13   0
1     1  15  14
2    14  19  14
3    19   5   1

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

Edit: Как сказал Энди, проблема, вероятно, в дублировании заголовков столбцов.

Для примера файла таблицы "dummy.csv" я составил:

Time    H1  N2  Time    N2  Time Relative
3   13  13  3   13  0
1   15  15  1   15  14
14  19  19  14  19  14
19  5   5   19  5   1

используя read_table, дает уникальные столбцы и работает правильно:

In [151]: df2 = pd.read_table('dummy.csv')
          df2
Out[151]:
         Time  H1  N2  Time.1  N2.1  Time Relative
      0     3  13  13       3    13              0
      1     1  15  15       1    15             14
      2    14  19  19      14    19             14
      3    19   5   5      19     5              1
In [152]: df2.T.drop_duplicates().T
Out[152]:
             Time  H1  Time Relative
          0     3  13              0
          1     1  15             14
          2    14  19             14
          3    19   5              1  

Если ваша версия не позволяет вам, вы можете взломать решение, чтобы сделать их уникальными:

In [169]: df2 = pd.read_table('dummy.csv', header=None)
          df2
Out[169]:
              0   1   2     3   4              5
        0  Time  H1  N2  Time  N2  Time Relative
        1     3  13  13     3  13              0
        2     1  15  15     1  15             14
        3    14  19  19    14  19             14
        4    19   5   5    19   5              1
In [171]: from collections import defaultdict
          col_counts = defaultdict(int)
          col_ix = df2.first_valid_index()
In [172]: cols = []
          for col in df2.ix[col_ix]:
              cnt = col_counts[col]
              col_counts[col] += 1
              suf = '_' + str(cnt) if cnt else ''
              cols.append(col + suf)
          cols
Out[172]:
          ['Time', 'H1', 'N2', 'Time_1', 'N2_1', 'Time Relative']
In [174]: df2.columns = cols
          df2 = df2.drop([col_ix])
In [177]: df2
Out[177]:
          Time  H1  N2 Time_1 N2_1 Time Relative
        1    3  13  13      3   13             0
        2    1  15  15      1   15            14
        3   14  19  19     14   19            14
        4   19   5   5     19    5             1
In [178]: df2.T.drop_duplicates().T
Out[178]:
          Time  H1 Time Relative
        1    3  13             0
        2    1  15            14
        3   14  19            14
        4   19   5             1 

Ответ 2

Все вышеизложенное кажется излишне тяжелым и утомительным методом - есть однострочное решение проблемы. Это применяется, если некоторые имена столбцов дублируются, и вы хотите их удалить:

df = df.loc[:,~df.columns.duplicated()]

[обновление] Как это работает:

Предположим, что столбцы кадра данных ['alpha','beta','alpha']

df.columns.duplicated() возвращает логический массив: a True или False для каждого столбца. Если это False, то имя столбца уникально до этой точки, если оно True, тогда имя столбца дублируется ранее. Например, используя данный пример, возвращаемое значение будет [False,False,True].

Pandas позволяет индексировать с использованием логических значений, в результате чего он выбирает только значения True. Поскольку мы хотим сохранить нерасшифрованные столбцы, нам нужно, чтобы вышеперечисленный логический массив был перевернут (т.е. [True, True, False] = ~[False,False,True])

Наконец, df.loc[:,[True,True,False]] выбирает только не дублированные столбцы, используя вышеупомянутую возможность индексирования.

Примечание: только вышеперечисленное проверяет имена колонок, а не значения столбцов.

Ответ 3

Если я не ошибаюсь, следующее делает то, что было задано без проблем с памятью решения транспонирования и с меньшим количеством строк, чем функция @kalu, сохраняя первый из столбцов с одинаковым именем.

Cols = list(df.columns)
for i,item in enumerate(df.columns):
    if item in df.columns[:i]: Cols[i] = "toDROP"
df.columns = Cols
df = df.drop("toDROP",1)

Ответ 4

Транспонирование неэффективно для больших DataFrames. Вот альтернатива:

def duplicate_columns(frame):
    groups = frame.columns.to_series().groupby(frame.dtypes).groups
    dups = []
    for t, v in groups.items():
        dcols = frame[v].to_dict(orient="list")

        vs = dcols.values()
        ks = dcols.keys()
        lvs = len(vs)

        for i in range(lvs):
            for j in range(i+1,lvs):
                if vs[i] == vs[j]: 
                    dups.append(ks[i])
                    break

    return dups       

Используйте его следующим образом:

dups = duplicate_columns(frame)
frame = frame.drop(dups, axis=1)

Edit

Эффективная версия памяти, которая обрабатывает nans как любое другое значение:

from pandas.core.common import array_equivalent

def duplicate_columns(frame):
    groups = frame.columns.to_series().groupby(frame.dtypes).groups
    dups = []

    for t, v in groups.items():

        cs = frame[v].columns
        vs = frame[v]
        lcs = len(cs)

        for i in range(lcs):
            ia = vs.iloc[:,i].values
            for j in range(i+1, lcs):
                ja = vs.iloc[:,j].values
                if array_equivalent(ia, ja):
                    dups.append(cs[i])
                    break

    return dups