Панни python видаляють повторювані стовпці


126

Який найпростіший спосіб видалити повторювані стовпці з фрейму даних?

Я читаю текстовий файл з повторюваними стовпцями через:

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. Будь-які пропозиції будуть вдячні.


Додаткові відомості

Версія 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

Яка версія панд у вас є? ( import pandas as pd; pd.__version__ )
beardc

1
@BirdJaguarIV, я використовую панди версії 0.9.0
Onlyjus

Ви можете спробувати оновити до 0,10. Моя версія робить колонки унікальними read_tableдля прикладу, який я склав.
beardc

Пам’ятайте, що df = df.T.drop_duplicates (). T не враховує назву стовпця. Якщо у вас є два стовпці з однаковими даними, але різними іменами, один з них буде видалено помилково.
Joylove

Відповіді:


392

Існує однолінійне рішення проблеми. Це застосовується, якщо деякі назви стовпців дублюються, і ви хочете їх видалити:

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]]вибирає лише не дублюються стовпці, використовуючи вищезгадану можливість індексації.

Примітка : вищевказане перевіряє лише назви стовпців, а не значення стовпців.


16
Ідеальна відповідь також буде працювати для дублюваних значень, а не лише для імен.
GrimSqueaker

7
@GrimSqueaker: Якщо ви хочете розглянути, чи значення дублюються, ви хочете щось подібне df.T.drop_duplicates().T.
Джон Цвінк

3
На сьогодні найшвидше рішення
AtotheSiv

2
@ VaidøtasIvøška дивіться другу відповідь на це запитання
Гена

2
@JohnZwinck: це працює лише для невеликих фреймів даних, оскільки існує обмеження на кількість стовпців. Наприклад, для мене це не вдалося, наприклад, для фрейму даних зі 100 000 рядками, оскільки це дає 100 000 стовпців після перенесення, що неможливо
Eelco van Vliet

40

Здається, ви вже знаєте унікальні назви стовпців. Якби це так, то 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

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

Редагувати: Як сказав Енді, проблема, ймовірно, з дублюючими заголовками стовпців.

Для файлу зразкової таблиці "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 

5
На жаль, df['Time']вибирає всі часові ряди (тобто повертає DataFrame), і df['Time', ..]це поверне весь DataFrame.
Енді Хайден

Так, це досить нудно ... сподіваємось, це лише різниця у версії.
beardc

2
Використання подвійних транспозитів може мати небажані побічні ефекти, такі як перетворення числових типів в об'єкти, якщо у вас є df зі змішаними типами. Див: stackoverflow.com/questions/24682396 / ...
Petergavinkin

Це рішення викликає у мене проблеми з великими рамками даних: RecursionError: maximum recursion depth exceeded
Скотт

Транспонування великого фрейму даних буде повільним процесом
Куш Пател

13

Транспонування неефективне для великих 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)

Редагувати

Ефективна у пам’яті версія, яка розглядає нан, як і будь-яке інше значення:

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

3
Працює як шарм, дуже ефективно! Використання my_df.T.drop_duplicates().Tзависне на великих фреймах даних.
Буде

1
Прекрасне рішення, але 26 квітня 2017 року я отримав /usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:17: DeprecationWarning: 'pandas.core.common.array_equivalent' is deprecated and is no longer public API
Джордж Фішер

заміщення if array_equivalent(ia, ja):на, if np.array_equal(ia, ja):здається, дає ті самі результати, але я прочитав, що він не працює з NaNs добре.
Джордж Фішер

@GeorgeFisher Чи базовий код array_equivalentвсе ще доступний у загальнодоступному репо, можливо, на старшій гілці?
калу

@kalu зараз є струм numpy.array_equiv; що стосується панд, я не бачу жодних гілок попереднього випуску на GitHub, pandas.core.commonале, можливо, є інші місця, де можна подивитися
Джордж Фішер

11

Якщо я не помиляюся, наступне робить те, що було запропоновано без проблем із пам'яттю транспонтованого рішення та з меншою кількістю рядків, ніж функція @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)

У моєму випадку ваше рішення не працює, воно показує мені: "ValueError: мітки ['toDROP'] не містяться в осі" після виконання останнього рядка
NuValue

4

Схоже, ви були на правильному шляху. Ось одноколісний лайнер, який ви шукали:

df.reset_index().T.drop_duplicates().T

Але оскільки не існує прикладу фрейму даних, який виробляє посилається повідомлення про помилку Reindexing only valid with uniquely valued index objects, важко точно сказати, що вирішило б проблему. якщо відновлення вихідного індексу важливо для вас, зробіть це:

original_index = df.index.names
df.reset_index().T.drop_duplicates().reset_index(original_index).T

0

Перший крок: - Прочитайте перший рядок, тобто всі стовпці, видаліть усі повторювані стовпці.

Другий крок: - Нарешті, прочитайте лише ці колонки.

cols = pd.read_csv("file.csv", header=None, nrows=1).iloc[0].drop_duplicates()
df = pd.read_csv("file.csv", usecols=cols)

0

Я зіткнувся з цією проблемою, коли один лайнер, наданий першою відповіддю, працював добре. Однак у мене виникло додаткове ускладнення, коли друга копія стовпця мала всі дані. Першого примірника не зробили.

Рішенням було створити два кадри даних, розділивши один кадр даних шляхом включення оператора заперечення. Після того, як у мене було два кадри даних, я запустив заяву про приєднання за допомогою lsuffix. Таким чином, я міг би потім посилатися та видаляти стовпчик без даних.

- Е


0

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

dupes = pd.DataFrame(df.columns)
dupes[dupes.duplicated()]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.