перемішування / перестановка DataFrame в пандах


78

Що таке простий та ефективний спосіб перетасувати кадр даних у пандах, за рядками чи стовпцями? Тобто як записати функцію, shuffle(df, n, axis=0)яка приймає фрейм даних, кількість перетасовувань nта вісь ( axis=0дорівнює рядкам, axis=1є стовпчиками) і повертає копію кадру даних, перемішану nразів.

Редагувати : ключ - це робити, не руйнуючи мітки рядків / стовпців фрейму даних. Якщо ви просто перетасуєте, df.indexщо втратить всю цю інформацію. Я хочу, щоб результат dfбув таким самим, як оригінал, за винятком того, що порядок рядків або порядок стовпців різний.

Edit2 : Моє запитання було незрозумілим. Коли я кажу про перемішування рядків, я маю на увазі перемішування кожного рядка незалежно. Отже, якщо у вас є два стовпці aі b, я хочу, щоб кожен рядок перемішався сам по собі, щоб у вас не було однакових асоціацій між собою, як aі bу вас, якщо ви просто перевпорядкували кожен рядок у цілому. Щось на зразок:

for 1...n:
  for each col in df: shuffle column
return new_df

Але, сподіваємось, ефективніше, ніж наївна циклічність. Це не працює для мене:

def shuffle(df, n, axis=0):
        shuffled_df = df.copy()
        for k in range(n):
            shuffled_df.apply(np.random.shuffle(shuffled_df.values),axis=axis)
        return shuffled_df

df = pandas.DataFrame({'A':range(10), 'B':range(10)})
shuffle(df, 5)


^ Ваша відповідь відповідає на питання, але, схоже, це не та відповідь, яку люди шукають
cs95

Відповіді:


40
In [16]: def shuffle(df, n=1, axis=0):     
    ...:     df = df.copy()
    ...:     for _ in range(n):
    ...:         df.apply(np.random.shuffle, axis=axis)
    ...:     return df
    ...:     

In [17]: df = pd.DataFrame({'A':range(10), 'B':range(10)})

In [18]: shuffle(df)

In [19]: df
Out[19]: 
   A  B
0  8  5
1  1  7
2  7  3
3  6  2
4  3  4
5  0  1
6  9  0
7  4  6
8  2  8
9  5  9

2
Як тут відрізнити рядки від перемішування стовпців?

Дякую .. Я пояснив своє незрозуміле запитання. Я прагну перетасувати по рядках незалежно від інших рядків - так перетасувати таким чином, щоб у вас не завжди було 1,5разом і 4,8разом (але також не просто перетасовка стовпців, яка обмежує вас двома варіантами)

14
попередження Я думав, що це df.apply(np.random.permutation)буде працювати як рішення df.reindex(np.random.permutation(df.index))і виглядає акуратніше, але насправді вони поводяться по-різному. Останній підтримує зв'язок між стовпцями одного рядка, перший - ні. Моє непорозуміння, звичайно, але, сподіваюся, це врятує інших людей від тієї ж помилки.
gozzilli

1
Що таке 'np' у цьому контексті?
Санки

1
нумпі. Це звичайно робити:import numpy as np
Аку

218

Використовуйте функцію numpy random.permuation:

In [1]: df = pd.DataFrame({'A':range(10), 'B':range(10)})

In [2]: df
Out[2]:
   A  B
0  0  0
1  1  1
2  2  2
3  3  3
4  4  4
5  5  5
6  6  6
7  7  7
8  8  8
9  9  9


In [3]: df.reindex(np.random.permutation(df.index))
Out[3]:
   A  B
0  0  0
5  5  5
6  6  6
3  3  3
8  8  8
7  7  7
9  9  9
1  1  1
2  2  2
4  4  4

25
+1, тому що це саме те, що я шукав (хоча виявляється, це не те, що хотів ОП)
Дуг Пол

4
Також можна використовувати, df.iloc[np.random.permutation(np.arange(len(df)))]якщо є дурні та інше (і це може бути швидше для мене).
Andy Hayden

3
Хороший метод. Чи є спосіб зробити це на місці?
Ендрю

3
Для мене (Python v3.6 та Pandas v0.20.1) мені довелося замінити df.reindex(np.random.permutation(df.index)), df.set_index(np.random.permutation(df.index))щоб отримати бажаний ефект.
Емануель

1
після того, set_indexяк Емануель, мені теж знадобивсяdf.sort_index(inplace=True)
Шаді

92

Вибірка рандомізується, тому просто відіберіть весь кадр даних.

df.sample(frac=1)

7
Зверніть увагу, якщо ви намагаєтеся перепризначити стовпець, використовуючи це, вам потрібно це зробитиdf['column'] = df['column'].sample(frac=1).reset_index(drop=True)
Кори Левінсон,

19

Ви можете використовувати sklearn.utils.shuffle()( для підтримки фреймів даних Pandas потрібен sklearn 0.16.1 або вище):

# Generate data
import pandas as pd
df = pd.DataFrame({'A':range(5), 'B':range(5)})
print('df: {0}'.format(df))

# Shuffle Pandas data frame
import sklearn.utils
df = sklearn.utils.shuffle(df)
print('\n\ndf: {0}'.format(df))

виходи:

df:    A  B
0  0  0
1  1  1
2  2  2
3  3  3
4  4  4


df:    A  B
1  1  1
0  0  0
3  3  3
4  4  4
2  2  2

Тоді ви можете використовувати df.reset_index()для скидання стовпця індексу, якщо це потрібно:

df = df.reset_index(drop=True)
print('\n\ndf: {0}'.format(df)

виходи:

df:    A  B
0  1  1
1  0  0
2  4  4
3  2  2
4  3  3

FYI, df.sample(frac=1)це незначно швидше (76,9 проти 78,9 мс для 400 тис. Рядків).
m-dz

6

З документів використовуйте sample():

In [79]: s = pd.Series([0,1,2,3,4,5])

# When no arguments are passed, returns 1 row.
In [80]: s.sample()
Out[80]: 
0    0
dtype: int64

# One may specify either a number of rows:
In [81]: s.sample(n=3)
Out[81]: 
5    5
2    2
4    4
dtype: int64

# Or a fraction of the rows:
In [82]: s.sample(frac=0.5)
Out[82]: 
5    5
4    4
1    1
dtype: int64

6

Просте рішення в пандах полягає у використанні sampleметоду незалежно на кожному стовпці. Використовуйте applyдля перебору кожного стовпця:

df = pd.DataFrame({'a':[1,2,3,4,5,6], 'b':[1,2,3,4,5,6]})
df

   a  b
0  1  1
1  2  2
2  3  3
3  4  4
4  5  5
5  6  6

df.apply(lambda x: x.sample(frac=1).values)

   a  b
0  4  2
1  1  6
2  6  5
3  5  3
4  2  4
5  3  1

Ви повинні використовувати .valueтак, щоб ви повернули масив numpy, а не серію, інакше повернута серія буде вирівнюватися до вихідного DataFrame, не змінюючи нічого:

df.apply(lambda x: x.sample(frac=1))

   a  b
0  1  1
1  2  2
2  3  3
3  4  4
4  5  5
5  6  6

4

Я вдався до легкої адаптації відповіді @root та безпосереднього використання вихідних значень. Звичайно, це означає, що ви втрачаєте можливість виконувати вигадливу індексацію, але вона чудово працює для простого перетасування даних.

In [1]: import numpy

In [2]: import pandas

In [3]: df = pandas.DataFrame({"A": range(10), "B": range(10)})    

In [4]: %timeit df.apply(numpy.random.shuffle, axis=0)
1000 loops, best of 3: 406 µs per loop

In [5]: %%timeit
   ...: for view in numpy.rollaxis(df.values, 1):
   ...:     numpy.random.shuffle(view)
   ...: 
10000 loops, best of 3: 22.8 µs per loop

In [6]: %timeit df.apply(numpy.random.shuffle, axis=1)
1000 loops, best of 3: 746 µs per loop

In [7]: %%timeit                                      
for view in numpy.rollaxis(df.values, 0):
    numpy.random.shuffle(view)
   ...: 
10000 loops, best of 3: 23.4 µs per loop

Зверніть увагу, що numpy.rollaxisпризводить зазначену вісь до першого виміру, а потім давайте перебиратимемо масиви з рештою розмірів, тобто, якщо ми хочемо перемішати вздовж першого виміру (стовпці), нам потрібно перекинути другий вимір вперед, щоб ми застосовуємо перемішування для переглядів першого виміру.

In [8]: numpy.rollaxis(df, 0).shape
Out[8]: (10, 2) # we can iterate over 10 arrays with shape (2,) (rows)

In [9]: numpy.rollaxis(df, 1).shape
Out[9]: (2, 10) # we can iterate over 2 arrays with shape (10,) (columns)

Потім ваша остаточна функція використовує фокус, щоб привести результат у відповідність з очікуванням застосування функції до осі:

def shuffle(df, n=1, axis=0):     
    df = df.copy()
    axis = int(not axis) # pandas.DataFrame is always 2D
    for _ in range(n):
        for view in numpy.rollaxis(df.values, axis):
            numpy.random.shuffle(view)
    return df

3

Це може бути кориснішим, коли ви хочете, щоб ваш індекс перемішався.

def shuffle(df):
    index = list(df.index)
    random.shuffle(index)
    df = df.ix[index]
    df.reset_index()
    return df

Він вибирає нові df за допомогою нового індексу, а потім скидає їх.


1

Я знаю, що питання стосується pandasdf, але у випадку, якщо перетасовка відбувається за рядками (порядок стовпців змінено, порядок рядків незмінний), тоді назви стовпців вже не мають значення, і може бути цікаво використовувати np.arrayзамість них, тоді np.apply_along_axis()буде те, що ви шукають.

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

Якщо назва кадру даних панда названа df, можливо, ви можете:

  1. отримати значення кадру даних за допомогою values = df.values,
  2. створити np.arrayзvalues
  3. застосуйте метод, показаний нижче, для перемішування np.arrayрядків або стовпців
  4. відтворити нову (перетасовану) панду df з перетасованої np.array

Оригінальний масив

a = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32],[40, 41, 42]])
print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]

Зберігайте порядок рядків, перемішуйте колони в кожному рядку

print(np.apply_along_axis(np.random.permutation, 1, a))
[[11 12 10]
 [22 21 20]
 [31 30 32]
 [40 41 42]]

Слідкуйте за порядком роботи, перемішуйте рядки в кожному стовпці

print(np.apply_along_axis(np.random.permutation, 0, a))
[[40 41 32]
 [20 31 42]
 [10 11 12]
 [30 21 22]]

Оригінальний масив незмінний

print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]

0

Ось робота, яку я знайшов, якщо ви хочете лише перетасувати підмножину DataFrame:

shuffle_to_index = 20
df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.