Pandas builds on NumPy to provide labeled, tabular data structures ideal for data analysis. The primary object is the DataFrame, which can be created directly from a dictionary such as pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]}). Once you have a DataFrame, the two main ways of selecting data are df.loc[] and df.iloc[]. The loc accessor is label-based, using the actual index values and column names, whereas iloc is purely integer-position based. For example, df.loc[0:3, ['name', 'age']] returns rows 0 through 3 together with only the 'name' and 'age' columns, while df.iloc[:5, :2] selects the first five rows and first two columns by position.
Boolean indexing is the standard way to filter rows in Pandas. Writing df[df['age'] > 30] yields a DataFrame containing only the rows where the condition holds. Multiple conditions can be combined by wrapping each one in parentheses and joining them with & (and) or | (or), as in df[(df['age'] > 25) & (df['city'] == 'NYC')]. Sorting is straightforward with df.sort_values('price', ascending=False), and you can sort by multiple columns with different orders using df.sort_values(['col1', 'col2'], ascending=[True, False]).
DataFrame indices are flexible and can be modified as needed. df.set_index('col') promotes a column to the row index, df.reset_index(drop=True) restores the default integer index while discarding the old one, and df.rename(columns={'old_name': 'new_name'}) renames selected columns. You can also create new columns directly through vectorized arithmetic, such as df['total'] = df['price'] * df['quantity'], or by applying a custom function element-wise with df['col'].apply(lambda x: x ** 2). These operations together make Pandas the most natural tool for the interactive slicing and dicing of tabular data.