Sự khác biệt giữa bản đồ, sơ đồ áp dụng và phương pháp áp dụng trong Pandas


465

Bạn có thể cho tôi biết khi nào nên sử dụng các phương pháp vector hóa này với các ví dụ cơ bản?

Tôi thấy đó maplà một Seriesphương thức trong khi phần còn lại là DataFramecác phương thức. Tôi đã nhầm lẫn về applyapplymapphương pháp mặc dù. Tại sao chúng ta có hai phương thức để áp dụng hàm cho DataFrame? Một lần nữa, các ví dụ đơn giản minh họa việc sử dụng sẽ là tuyệt vời!


5
Sửa lỗi cho tôi nếu tôi sai, nhưng tôi tin rằng các hàm đó không phải là phương thức vector hóa vì tất cả chúng đều liên quan đến một vòng lặp trên các phần tử mà chúng được áp dụng.
Tanguy

1
Tôi không thể thấy sự khác biệt ở đây: gist.github.com/MartinThoma/e320cbb937afb4ff766f75988f1c65e6
Martin Thoma

Câu trả lời:


533

Trực tiếp từ cuốn sách Phân tích dữ liệu Python của Wes McKinney , pg. 132 (Tôi rất khuyến khích cuốn sách này):

Một hoạt động thường xuyên khác là áp dụng một hàm trên mảng 1D cho mỗi cột hoặc hàng. Phương thức áp dụng của DataFrame thực hiện chính xác điều này:

In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [117]: frame
Out[117]: 
               b         d         e
Utah   -0.029638  1.081563  1.280300
Ohio    0.647747  0.831136 -1.549481
Texas   0.513416 -0.884417  0.195343
Oregon -0.485454 -0.477388 -0.309548

In [118]: f = lambda x: x.max() - x.min()

In [119]: frame.apply(f)
Out[119]: 
b    1.133201
d    1.965980
e    2.829781
dtype: float64

Nhiều thống kê mảng phổ biến nhất (như tổng và trung bình) là các phương thức DataFrame, vì vậy sử dụng áp dụng là không cần thiết.

Các hàm Python khôn ngoan cũng có thể được sử dụng. Giả sử bạn muốn tính một chuỗi được định dạng từ mỗi giá trị dấu phẩy động trong khung. Bạn có thể làm điều này với applicationmap:

In [120]: format = lambda x: '%.2f' % x

In [121]: frame.applymap(format)
Out[121]: 
            b      d      e
Utah    -0.03   1.08   1.28
Ohio     0.65   0.83  -1.55
Texas    0.51  -0.88   0.20
Oregon  -0.49  -0.48  -0.31

Lý do cho việc áp dụng tên là Sê-ri có một phương pháp bản đồ để áp dụng một hàm phần tử khôn ngoan:

In [122]: frame['e'].map(format)
Out[122]: 
Utah       1.28
Ohio      -1.55
Texas      0.20
Oregon    -0.31
Name: e, dtype: object

Tóm tắt, applyhoạt động trên cơ sở hàng / cột của DataFrame, applymaphoạt động theo yếu tố trên DataFrame và maphoạt động theo yếu tố trên Series.


31
Nói đúng ra, applymap nội bộ được thực hiện thông qua áp dụng với một tham số chức năng trên thông qua wrap-up chút (rougly nói thay funccho lambda x: [func(y) for y in x], và áp dụng cột-khôn ngoan)
Alko

5
Cảm ơn đã giải thích. Vì mapapplymapcả hai đều hoạt động theo yếu tố, tôi sẽ mong đợi một phương thức duy nhất (hoặc maphoặc applymap) sẽ hoạt động cả cho Sê-ri và Khung dữ liệu. Có lẽ có những cân nhắc thiết kế khác, và Wes McKinney đã quyết định đưa ra hai phương pháp khác nhau.
thúc

2
Đó là trên trang 129 trong bản sao của tôi vì một số lý do. Không có nhãn cho phiên bản thứ hai hoặc bất cứ điều gì.
Jody

1
Có cách nào để làm applymapcùng với groupbychức năng trong gấu trúc không?
everestial007

Làm thế nào để áp dụng một chức năng trên dữ liệu cột theo nhóm?
hhh

83

So sánh map, applymapvà : Các vấn đề Contextapply

Sự khác biệt lớn đầu tiên: ĐỊNH NGH

  • map được định nghĩa trên Sê-ri CHỈ
  • applymap được định nghĩa trên DataFrames CHỈ
  • apply được định nghĩa trên CẢ

Sự khác biệt lớn thứ hai: INPUT ARGUMENT

  • mapchấp nhận dicts Series, hoặc có thể gọi
  • applymapvà chỉ applychấp nhận các cuộc gọi

Sự khác biệt lớn thứ ba: BEHAVIOR

  • map là phần tử cho Series
  • applymap là phần tử cho DataFrames
  • applycũng hoạt động theo nguyên tố nhưng phù hợp với các hoạt động và tổng hợp phức tạp hơn. Hành vi và giá trị trả về phụ thuộc vào chức năng.

Sự khác biệt lớn thứ tư (quan trọng nhất): SỬ DỤNG TRƯỜNG HỢP

  • mapcó nghĩa là để ánh xạ các giá trị từ miền này sang miền khác, do đó được tối ưu hóa cho hiệu suất (ví dụ df['A'].map({1:'a', 2:'b', 3:'c'}):)
  • applymaplà tốt cho các phép biến đổi theo phần tử trên nhiều hàng / cột (ví dụ df[['A', 'B', 'C']].applymap(str.strip):)
  • applylà để áp dụng bất kỳ chức năng nào không thể được vector hóa (ví dụ, df['sentences'].apply(nltk.sent_tokenize))

Tóm tắt

nhập mô tả hình ảnh ở đây

Chú thích

  1. mapkhi được thông qua một từ điển / Sê-ri sẽ ánh xạ các thành phần dựa trên các khóa trong từ điển / Sê-ri đó. Các giá trị thiếu sẽ được ghi lại dưới dạng NaN trong đầu ra.
  2. applymaptrong các phiên bản gần đây đã được tối ưu hóa cho một số hoạt động. Bạn sẽ tìm thấy applymapmột chút nhanh hơn applytrong một số trường hợp. Đề nghị của tôi là kiểm tra cả hai và sử dụng bất cứ điều gì tốt hơn.

  3. mapđược tối ưu hóa cho ánh xạ phần tử và chuyển đổi. Các hoạt động liên quan đến từ điển hoặc Sê-ri sẽ cho phép gấu trúc sử dụng các đường dẫn mã nhanh hơn để có hiệu suất tốt hơn.

  4. Series.applytrả về một vô hướng cho các hoạt động tổng hợp, Series khác. Tương tự cho DataFrame.apply. Lưu ý rằng applycũng có fastpaths khi được gọi với chức năng NumPy nhất định như mean, sumvv

70

Có những thông tin tuyệt vời trong những câu trả lời này, nhưng tôi đang thêm chính mình để tóm tắt rõ ràng phương pháp nào hoạt động theo mảng so với khôn ngoan theo yếu tố. jeremiahbuddha chủ yếu làm điều này nhưng không đề cập đến Series.apply. Tôi không có đại diện để bình luận.

  • DataFrame.apply hoạt động trên toàn bộ hàng hoặc cột tại một thời điểm.

  • DataFrame.applymap, Series.applySeries.maphoạt động trên một yếu tố tại thời điểm.

Có rất nhiều sự chồng chéo giữa các khả năng của Series.applySeries.map, có nghĩa là một trong hai sẽ hoạt động trong hầu hết các trường hợp. Họ có một số khác biệt nhỏ, một số trong đó đã được thảo luận trong câu trả lời của osa.


38

Thêm vào các câu trả lời khác, trong Seriesđó cũng có bản đồáp dụng .

Áp dụng có thể tạo một DataFrame trong một chuỗi ; tuy nhiên, bản đồ sẽ chỉ đưa một loạt vào mỗi ô của một chuỗi khác, đây có thể không phải là điều bạn muốn.

In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0    1
1    2
2    3
dtype: int64

In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]: 
   0  1
0  1  1
1  2  2
2  3  3

In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]: 
0    0    1
1    1
dtype: int64
1    0    2
1    2
dtype: int64
2    0    3
1    3
dtype: int64
dtype: object

Ngoài ra nếu tôi có một chức năng với các tác dụng phụ, chẳng hạn như "kết nối với máy chủ web", có lẽ tôi sẽ sử dụng applychỉ vì mục đích rõ ràng.

series.apply(download_file_for_every_element) 

Mapcó thể sử dụng không chỉ một chức năng, mà còn từ điển hoặc một bộ khác. Giả sử bạn muốn thao tác hoán vị .

Lấy

1 2 3 4 5
2 1 4 5 3

Bình phương của hoán vị này là

1 2 3 4 5
1 2 5 3 4

Bạn có thể tính toán nó bằng cách sử dụng map. Không chắc chắn nếu tự ứng dụng được ghi lại, nhưng nó hoạt động trong 0.15.1.

In [39]: p=pd.Series([1,0,3,4,2])

In [40]: p.map(p)
Out[40]: 
0    0
1    1
2    4
3    2
4    3
dtype: int64

3
Ngoài ra, .apply () cho phép bạn chuyển các kwarg vào hàm trong khi .map () thì không.
neilxdims

19

@jeremiahbuddha đã đề cập rằng áp dụng hoạt động trên hàng / cột, trong khi applicationmap hoạt động theo yếu tố. Nhưng có vẻ như bạn vẫn có thể sử dụng áp dụng cho tính toán phần tử khôn ngoan ....

    frame.apply(np.sqrt)
    Out[102]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

    frame.applymap(np.sqrt)
    Out[103]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

29
Bắt tốt với điều này. Lý do điều này hoạt động trong ví dụ của bạn là vì np.sqrt là một ufunc, tức là nếu bạn cho nó một mảng, nó sẽ phát hàm sqrt lên từng phần tử của mảng. Vì vậy, khi áp dụng đẩy np.sqrt trên mỗi cột, np.sqrt tự hoạt động trên từng thành phần của các cột, do đó về cơ bản bạn sẽ nhận được kết quả tương tự như áp dụng.
jeremiahbuddha

11

Chỉ muốn chỉ ra, khi tôi vật lộn với điều này một chút

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()

điều này không tự sửa đổi khung dữ liệu, phải được chỉ định lại

df = df.applymap(f)
df.describe()

1
Đôi khi tôi gặp khó khăn trong việc tìm hiểu xem bạn có phải phân công lại hay không sau khi làm điều gì đó với df. Nó chủ yếu là bản dùng thử và lỗi đối với tôi, nhưng tôi cá là có logic để nó hoạt động (mà tôi đang bỏ lỡ).
marillion

2
nói chung, khung dữ liệu gấu trúc chỉ được sửa đổi bằng cách gán lại df = modified_dfhoặc nếu bạn đặt inplace=Truecờ. Ngoài ra, khung dữ liệu sẽ thay đổi nếu bạn chuyển một khung dữ liệu cho một hàm theo tham chiếu và hàm này sửa đổi khung dữ liệu
muon

1
Điều này không hoàn toàn đúng, hãy nghĩ .ixhoặc .wherev.v. Không chắc lời giải thích đầy đủ là gì khi bạn cần gán lại và khi nào không.
Thanos

10

Có lẽ giải thích đơn giản nhất sự khác biệt giữa áp dụng và sơ đồ áp dụng:

áp dụng lấy toàn bộ cột làm tham số và sau đó gán kết quả cho cột này

applicationmap lấy giá trị ô riêng làm tham số và gán kết quả trở lại ô này.

NB Nếu áp dụng trả về giá trị đơn, bạn sẽ có giá trị này thay vì cột sau khi gán và cuối cùng sẽ chỉ có một hàng thay vì ma trận.


3

Sự hiểu biết của tôi:

Từ quan điểm chức năng:

Nếu hàm có các biến cần so sánh trong một cột / hàng, hãy sử dụng apply.

ví dụ : lambda x: x.max()-x.mean().

Nếu chức năng được áp dụng cho từng thành phần:

1> Nếu một cột / hàng được định vị, sử dụng apply

2> Nếu áp dụng cho toàn bộ khung dữ liệu, hãy sử dụng applymap

majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)

def times10(x):
  if type(x) is int:
    x *= 10 
  return x
df2.applymap(times10)

Vui lòng cung cấp df2 để rõ ràng hơn để chúng tôi có thể kiểm tra mã của bạn.
Ashish Anand

1

Dựa trên câu trả lời của cs95

  • map được định nghĩa trên Sê-ri CHỈ
  • applymap được định nghĩa trên DataFrames CHỈ
  • apply được định nghĩa trên CẢ

đưa ra vài ví dụ

In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [4]: frame
Out[4]:
            b         d         e
Utah    0.129885 -0.475957 -0.207679
Ohio   -2.978331 -1.015918  0.784675
Texas  -0.256689 -0.226366  2.262588
Oregon  2.605526  1.139105 -0.927518

In [5]: myformat=lambda x: f'{x:.2f}'

In [6]: frame.d.map(myformat)
Out[6]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [7]: frame.d.apply(myformat)
Out[7]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [8]: frame.applymap(myformat)
Out[8]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93

In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93


In [10]: myfunc=lambda x: x**2

In [11]: frame.applymap(myfunc)
Out[11]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

In [12]: frame.apply(myfunc)
Out[12]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

0

FOMO:

Ví dụ sau đây cho thấy applyapplymapáp dụng cho a DataFrame.

mapchức năng là thứ bạn chỉ áp dụng trên Series. Bạn không thể áp dụng map trên DataFrame.

Điều cần nhớ là applycó thể làm bất cứ điều gì applymap có thể, nhưng applycó các tùy chọn eXtra .

Các tùy chọn yếu tố X là: axisresult_typenơi result_typechỉ hoạt động khi axis=1(đối với cột).

df = DataFrame(1, columns=list('abc'),
                  index=list('1234'))
print(df)

f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only

# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1))  # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result

Là một sidenote, maphàm Series , không nên nhầm lẫn với maphàm Python .

Cái đầu tiên được áp dụng trên Sê-ri, để ánh xạ các giá trị và cái thứ hai cho mọi mục của một lần lặp.


Cuối cùng, đừng nhầm lẫn applyphương thức dataframe với applyphương thức nhóm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.