3895 words

Menu

About Link 2 Link 3

About

About this blog

An „Ion Fury“ ist alles retro: Action, Technik und Humor

Die Macher des legendären „Duke Nukem 3D“ legen 23 Jahre später mit „Ion Fury“ eine furiose Portion Nostalgie nach - leider mit Altlasten.

Auf Facebook teilen Auf Twitter teilen
Von Rainer Sigl

Die Neunzigerjahre sind längst wieder da, zumindest in Sachen First-Person-Shooter. In den letzten Monaten hat sich eine ganze Reihe famoser Indie-Titel der knallharten, geradlinigen Shooter-Vergangenheit wieder angenommen.

Jetzt steht aber statt lauwarmer Erinnerungskost sozusagen ein Original wieder von den Toten auf: Mit „Ion Fury“ veröffentlicht das legendäre Entwicklerstudio 3D Realms, Schöpfer des großen „Duke Nukem 3D“, ein brandneues Spiel, und das stilecht mit Originaltechnologie aus den 90er Jahren. Das bedeutet glorreiche Pixel-Explosionen, markige Sprüche und erstaunlich frischen Spielspaß.

Ionen-Maid mit 90er-Jahre-Humor

Die Handlung von „Ion Fury“ ist ziemlich egal, bemerkenswert ist nur, dass es diesmal kein stereotyper Macho-Muskelprotz, sondern eine Actionheldin ist, die immer größere Schusswaffen gegen böse Soldaten, Monster und Roboter zum Einsatz bringt. Link Test

Eigentlich hätte das Spiel deshalb auch „Ion Maiden“ heißen sollen, dagegen hat sich die legendäre Metalband aber im letzten Moment gerichtlich zur Wehr gesetzt. Anscheinend haben die englischen Gitarren-Großpapas weniger Sinn für den seichten Popcorn-Humor, der das Science-Fiction-Geballer vom plumpen Action-Gemetzel zum hoffnungsfrohen Trash-Kult werden lässt.

Umso deprimierender, dass sich in den letzten Tagen eine unschöne Kontroverse über sexistische und schwulenfeindliche Gags im Spiel entsponnen hat, bei der sich die Macher nach einigem Hin und Her enttäuschenderweise schlussendlich doch geweigert haben, dementsprechende Passagen im Spiel zu ändern.

Alles wie früher - im Guten wie im Schlechten

„Ion Fury“ wartet spielerisch mit alten Tugenden auf: Es gibt unkomplizierte Action, viele, gut versteckte Secrets und simple, aber spannende Schussgefechte in riesigen, recht verschachtelten Levels. Mehr durfte man 1996 nicht erwarten - und auch 2019 hat man seinen Spaß daran. So gesehen ist „Ion Fury“ gelungener Retro ohne Tiefgang, der ins Gedächtnis ruft, wie toll das damals eigentlich war - auch ohne Hochglanzgrafik und Physik-Engine.

„Ion Fury“ ist für Windows und Linux erschienen.

Was auch an die 90er erinnert, dabei aber weniger lustig ist, ist die unerfreulich verlaufene Debatte um homophobe Witzchen, die man im Boys Club - zwinker, zwinker - zwar eh nicht bös gemeint haben will, aber dann aus einer Mischung aus Trotz, Angst vor den rabiaten Fans und halbherzigem Verweis auf künstlerische Freiheit doch auch noch unbedingt mit im Spiel drin haben musste.

Das alles hätte man gern in den 90ern lassen dürfen - es war nicht alles leiwand, damals, in der guten, alten Zeit.

Sets

Sets


Symmetric Difference

Task
Given sets of integers, and , print their symmetric difference in ascending order. The term symmetric difference indicates those values that exist in either or but do not exist in both.

Input Format
The first line of input contains an integer, .
The second line contains space-separated integers.
The third line contains an integer, .
The fourth line contains space-separated integers.

Output Format
Output the symmetric difference integers in ascending order, one per line.

Sample Input
4
2 4 5 9
4
2 4 11 12

Sample Output
5
9
11
12

My solution

m, n = [set(input().split()) for i in range(4)][1::2]
result = m.difference(n)
result.update(n.difference(m))
print(*sorted(map(int, result)), sep = '\n')

Set .add()

Task
Apply your knowledge of the .add() operation to help your friend Rupal.Rupal has a huge collection of country stamps. She decided to count the total number of distinct country stamps in her collection. She asked for your help. You pick the stamps one by one from a stack of country stamps.
Find the total number of distinct country stamps.

Input Format
The first line contains an integer , the total number of country stamps.
The next lines contains the name of the country where the stamp is from.

Output Format
Output the total number of distinct country stamps on a single line.

Sample Input
7
UK
China
USA
France
New Zealand
UK
France

Sample Output
5

My solution

n, stamps = int(input()), set()
[stamps.add(input()) for i in range(n)]
print(len(stamps))

Set .discard(), .remove() & .pop()

Task
You have a non-empty set , and you have to execute commands given in lines.
The commands will be pop, remove and discard.

Input Format

The first line contains integer , the number of elements in the set .
The second line contains space separated elements of set . All of the elements are non-negative integers, less than or equal to 9.
The third line contains integer , the number of commands.
The next lines contains either pop, remove and/or discard commands followed by their associated value.

Output Format
Print the sum of the elements of set on a single line.

Sample Input
9
1 2 3 4 5 6 7 8 9
10
pop
remove 9
discard 9
discard 8
remove 7
pop
discard 6
remove 5
pop
discard 5
Sample Output

4

My solution

code

Adapted solution

code

itertools

itertools.permutations()

itertools.permutations(iterable[, r])

This tool returns successive length permutations of elements in an iterable. If is not specified or is None, then defaults to the length of the iterable, and all possible full length permutations are generated. Permutations are printed in a lexicographic sorted order. So, if the input iterable is sorted, the permutation tuples will be produced in a sorted order.

My solution

from itertools import permutations
inp = input().split()
s, k, result = inp[0], int(inp[1]), []
[result.append(''.join(list(i))) for i in list(permutations(s, k))]
result.sort()
[print(i) for i in result]

Adapted solution

from itertools import permutations
s, k = input().split()
print(*[''.join(i) for i in permutations(sorted(s), int(k))], sep='\n')

itertools.combinations()

itertools.combinations(iterable, r)
This tool returns the length subsequences of elements from the input iterable. Combinations are emitted in lexicographic sorted order. So, if the input iterable is sorted, the combination tuples will be produced in sorted order.

My solution

from itertools import combinations
s, k = input().split()
print(*[''.join(j) for i in range(int(k)+1) for j in combinations(sorted(s), i)
if ''.join(j) != ''], sep='\n')

Adapted solution

no adaptions

cmath

cmath

cmath.phase

This tool returns the phase of complex number z (also known as the argument of z).

>>>phase(complex(-1.0, 0.0))
3.1415926535897931

## cmath.abs
This tool returns the modulus (absolute value) of complex number z.

You are given a complex . Your task is to convert it to polar coordinates.

My solution


Adapted solution

code

cmath.polar()

Task
You are given a complex number z . Your task is to convert it to polar coordinates.
Input Format
A single line containing the complex number z. Note: complex() function can be used in python to convert the input as a complex number.
Constraints
Given number is a valid complex number
Output Format
Output two lines:
The first line should contain the value of .
The second line should contain the value of .

My solution

import cmath
print(*cmath.polar(complex(input())), sep='\n')

Adapted solution

code

C3-4: Pandas

C3-4: Pandas


L1: Anaconda


L2: Jupyter Notebooks


L3: NumPy


L4: Pandas

Creating Pandas Series

import pandas as pd | Import Pandas

my_series = pd.Series(data = ['data1', 'data2', data3', 'data4'], index = ['row1', 'row2', 'row3', 'row4']) | Creates Series

my_series # Display Series

my_series.shape # Show shape as tuple

my_series.ndim # Dimensions as int

my_series.size # Number of elements

my_series.index # Show index

my_seriex.values # Show values

x = bananas in my_series # Check if 'bananas' is an index label, returns True or False

# We import Pandas as pd into Python
import pandas as pd

# We create a Pandas Series that stores a grocery list
groceries = pd.Series(data = [30, 6, 'Yes', 'No'], index = ['eggs', 'apples', 'milk', 'bread'])

# We display the Groceries Pandas Series
groceries

eggs 30

apples 6

milk Yes

bread No

dtype: object

# We print some information about Groceries
print('Groceries has shape:', groceries.shape)
print('Groceries has dimension:', groceries.ndim)
print('Groceries has a total of', groceries.size, 'elements')

Groceries has shape: (4,)

Groceries has dimension: 1

Groceries has a total of 4 elements

# We print the index and data of Groceries
print('The data in Groceries is:', groceries.values)
print('The index of Groceries is:', groceries.index)

The data in Groceries is: [30 6 'Yes' 'No']

The index of Groceries is: Index(['eggs', 'apples', 'milk', 'bread'], dtype='object')

# We check whether bananas is a food item (an index) in Groceries
x = 'bananas' in groceries

# We check whether bread is a food item (an index) in Groceries
y = 'bread' in groceries

# We print the results
print('Is bananas an index label in Groceries:', x)
print('Is bread an index label in Groceries:', y)

Is bananas an index label in Groceries: False

Is bread an index label in Groceries: True

Accessing and Deleting Elements in Pandas

my_series['apples'] # Access element using a single label

my_series[['apples', 'pears']] # Access element using two or more labels

.loc | .iloc # The attribute .loc stands for location and it is used to explicitly state that we are using a labeled index. Similarly, the attribute .iloc stands for integer location and it is used to explicitly state that we are using a numerical index. Let's see some examples:

# We access elements in Groceries using index labels:

# We use a single index label
print('How many eggs do we need to buy:', groceries['eggs'])
print()

# we can access multiple index labels
print('Do we need milk and bread:\n', groceries[['milk', 'bread']]) 
print()

# we use loc to access multiple index labels
print('How many eggs and apples do we need to buy:\n', groceries.loc[['eggs', 'apples']]) 
print()

# We access elements in Groceries using numerical indices:

# we use multiple numerical indices
print('How many eggs and apples do we need to buy:\n',  groceries[[0, 1]]) 
print()

# We use a negative numerical index
print('Do we need bread:\n', groceries[[-1]]) 
print()

# We use a single numerical index
print('How many eggs do we need to buy:', groceries[0]) 
print()
# we use iloc to access multiple numerical indices
print('Do we need milk and bread:\n', groceries.iloc[[2, 3]]) 

How many eggs do we need to buy: 30

Do we need milk and bread:
milk Yes
bread No
dtype: object

How many eggs and apples do we need to buy:
eggs 30
apples 6
dtype: object

How many eggs and apples do we need to buy:
eggs 30
apples 6
dtype: object

Do we need bread:
bread No
dtype: object

How many eggs do we need to buy: 30

Do we need milk and bread:
milk Yes
bread No
dtype: object

Pandas Series are also mutable like NumPy ndarrays, which means we can change the elements of a Pandas Series after it has been created. For example, let's change the number of eggs we need to buy from our grocery list.

# We display the original grocery list
print('Original Grocery List:\n', groceries)

# We change the number of eggs to 2
groceries['eggs'] = 2

# We display the changed grocery list
print()
print('Modified Grocery List:\n', groceries)

Original Grocery List:
eggs 30
apples 6
milk Yes
bread No
dtype: object

Modified Grocery List:
eggs 2
apples 6
milk Yes
bread No
dtype: object

We can also delete items from a Pandas Series by using the .drop() method. The Series.drop(label) method removes the given label from the given Series. We should note that the Series.drop(label) method drops elements from the Series out of place, meaning that it doesn't change the original Series being modified. Let's see how this works:

# We display the original grocery list
print('Original Grocery List:\n', groceries)

# We remove apples from our grocery list. The drop function removes elements out of place
print()
print('We remove apples (out of place):\n', groceries.drop('apples'))

# When we remove elements out of place the original Series remains intact. To see this
# we display our grocery list again
print()
print('Grocery List after removing apples out of place:\n', groceries)

Original Grocery List:
eggs 30
apples 6
milk Yes
bread No
dtype: object

We remove apples (out of place):
eggs 30
milk Yes
bread No
dtype: object

Grocery List after removing apples out of place:
eggs 30
apples 6
milk Yes
bread No
dtype: object

We can delete items from a Pandas Series in place by setting the keyword inplace to True in the .drop() method. Let's see an example:

# We display the original grocery list
print('Original Grocery List:\n', groceries)

# We remove apples from our grocery list in place by setting the inplace keyword to True
groceries.drop('apples', inplace = True)

# When we remove elements in place the original Series its modified. To see this
# we display our grocery list again
print()
print('Grocery List after removing apples in place:\n', groceries)

Original Grocery List:
eggs 30
apples 6
milk Yes
bread No
dtype: object

Grocery List after removing apples in place:
eggs 30
milk Yes
bread No
dtype: object

Arithmetic Operations on Pandas Series

C3-5: Matplotlib

L5: Matplotlib and Seaborn Part 1


Bar Charts

\
Imports

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sb

%matplotlib inline

\
Read in CSV file

df = pd.read_csv('pokemon.csv')
print(df.shape)
pokemon.head(10)

\
Draw bar chart

sb.countplot(data = df, x = 'cat_var');

\
Change bar color to blue

base_color = sb.color_palette()[0]
sb.countplot(data = df, x = 'cat_var', color = base_color)

\
Change bar order (nominal-type data)

cat_order = df['cat_var'].value_counts().index
sb.countplot(data = df, x = 'cat_var', color = base_color, order = cat_order)

\
Change bar order (ordinal-type data)

level_order =['Alpha', 'Beta', 'Gamma', 'Delta']
ordered_cat = pd.api.types.CategoricalDtype(ordered = True, categories = level_order)
df['cat_var'] = df['cat_var'].astype(ordered_cat)

\
Horizontal bar chart

sb.countplot(data = df, y = 'cat_var', color = base_color)

\
Change rotation of x-ticks via matplotlib's xticks function

sb.countplot(data = df, x = 'cat_var'. color = base_color)
plt.xticks(rotation = 90)

Absolute vs. relative frequency

\
Relative frequency: Calculate proportion

n_points = df.shape[0]
max_count = df['cat_var'].value_counts().max()
max_prop = max_count / n_points

\
Relative frequency: Generate tick mark location and names

tick_props = np.arange(0, max_prop, 0.05)
tick_names = ['{:0.2f}'.format(v) for v in tick_props]

\
Create plot

sb.countplot(data = df, x = 'cat_var', color = base_color)
plt.yticks(tick_props * n_points, tick_names)
plt.ylabel('proportion')

\
Text annotations to label frequencies:
Create plot

sb.countplot(data = df, x = 'cat_var', color = base_color)

\
Add annotations

n_points = df.shape[0]
cat_counts = df['cat_var'].value_counts()
locs, labels = plt.xticks # get current tick locations and labels

\
Loop through each pair of locations and labels

for loc, label in zip(locs, labels):

        # get text property for the label to get the correct count
        count = cat_counts[label.get_text()]
        pct_string = '{:0.1f}%'.format(100*count/n_points)

        # print the annotation just below the top of the bar
        plt.text(loc, count-8, pct_string, ha = 'center', color = 'w')

Count missing data

\
Count missing data in each column

na_counts = df.isna().sum()

\
Seaborn barplot: Depict a summary of one quantitative variable against levels of a second qualitative variable

base_color = sb.color_palette()[0]
sb.barplot(na_counts.index.values, na_counts, color = base3_color)

Pie charts

\
Draw pie chart - data needs to be in a summarized form

sorted_counts = df['cat_var'].value_counts()
plt.pie(sorted_counts, labels = sorted_counts.index, startangle = 90, counterclock = False)

# scaling of the plot is equal on both x and y axes, could be oval shaped without this
plt.axis('square') 

\
Donut plot

sorted_counts = df['cat_var'].value_counts()
plt.pie(sorted_counts, labels = sorted_counts.index, startangle = 90, counterclock = False, wedgeprops = {'width' : 0.4};
plt.axis('square')

Histograms

\
Draw histogram (matplotlib)

plt.hist(data = df, x = 'num_var')

\
Set bin edges manually

bin_edges = np.arange(0, df['num_var'].max() + 1, 1)
prt.hist(data = df, x = 'num_var', bins = bin_edges)

\
Create subplot

# 1 row, 2 columns, subplot 1
plt.subplot(1, 2, 1)
# 1 row, 2 columns, subplot 2
plt.subplot(1, 2, 2)

\
distplot

sb.distplot(df['num_var'])

\
distplot without curve, transparency turned off

sb.distplot(['num_var'], bins = bin_edges, kde = False, hist_kws = {'alpha' : 1})

Figures, Axes, and Subplots

Set up figures and axes explicitly in matplotlib

fig = plt.figure()
ax = fig.add_axes([.125, .125, .775, .755])
ax.hist(data = df, x = 'num_var')

\
Use figures and axes in seaborn

fig = plt. figure()
ax = fig.add_axes([.125, .125, .775, .755])
base_color = sb.color_palette()[0]
sb.countplot(data = df, x = 'cat_var', color = base_color, ax = ax)

\
Subplots
Set figure size in inches (larger than normal)

plt.figure(figsize = [10, 5])

\
Create new axes on figure (1 row, 2 cols, subplot 1 and 2)

plt.subplot(1, 2, 1)
plt.subplot(1, 2, 2)

\
Retrieve current axes

ax = plt.gca()

\
Get a list of all axes in a figure

axes = fig.get_axes()

\
Create subplots

fig.add_subplot()

\
Create various subplots

fig, axes = plt.subplots(3, 4) # grid of 12 subplots
axes = axes.flatten() # 3 x 4 array => 12-element vector
for i in range(12):
    plt.sca(axes[i]) # set current axes
        plt.text(0.5, 0.5, i+1) # print subplot index no. to middle of axes

Choosing a plot for discrete data

Non-connected bins with rwidth (not suitable for continuous numeric data)

bin_edges = np.arange(1.5, 12.5+1, 1)
plt.hist(die_rolls, bins = bin_edges, rwidth = 0.7)
plt.xticks(np.arange(2, 12+1, 1)

Descriptive statistics, outliers and axis limits

matplotlib xlim to change histogram's axis limits

plt.figure(figsize = [10, 5])
bin_edges = np.arange(0, 35+1, 1)
plt.hist(data = df, x = 'skew_var', bins = bin_edges)
plt.xlim(o, 35)

Scales and transformations

Using a log10 axis => Problem: Readibility

log_data = np.log10(data)
log_bin_edges = np.arange(0.8, log_data.max()+0.1, 0.1)
plt.hist(log_data, bins = log_bin_edges)
plt.xlabel('log(values)')

\
Scale transformations with matplotlib's xscale function => Problem: Bins are too large

bin_edges = np.arange(0, data.max()+100, 100)
plt.hist(data, bins = bin_edges)
plt.xsclae('log')

\
Evenly spaced powers of 10 as scale

bin_edges = 10 ** np.arange(0.8, np.log10(data.max()) + 0.1, 0,1)
plt.hist(data, bins = bin_edges)
plt.xscale('log')
tick_locs = [10, 30, 100, 300, 1000, 3000]
plt.xticks(tick_locs, tick_locs)

Extra: Kernel density estimation (KDE)

KDE on top of histogram

sb.distplot(df['num_var'])

L6: Matplotlib and Seaborn Part 2

Scatterplots and correlation

matplotlib scatterplot: relationship between two numeric variables

plt.scatter(data = df, x >= 'num_var1', y = 'num_var2')

\
Seaborn's regplot for scatterplot with regression function fitting:
Standard: Linear regression function and shaded confidence region for the regression estimate

sb.regplot(data = df, x = 'num_var1', y = 'num_var2')

reg_fit = False => turn off regression line
\
Plot regression line => data needs to be adapted:

def log_trans(x, inverse = False):
    if not inverse: return np.log10(x)
        else: return np.power(10, x)

sb.regplot(df['num_var1'], df['num_var2'].apply(log_trans))
tick_locs = [10, 20, 50, 100, 200, 500]
plt.yticks(log_trans(tick_locs), tick_locs)

Overplotting, transparency, and jitter

Adding transparency to scatterplot using matplotlib's alpha parameter (0 = fully transparent, 1 = fully opaque)

plt.scatter(data = df, x = 'Ädisc_var1', y = 'disc_var2', alpha = 1/5)

\
Adding jitter with seaborn's regplot function (x_jitter and y_jitter)

sb.regplot(data = df, x = 'disc_var1', y = 'disc_var2', fit_reg = False, x_fitter = 0.2, y_jitter = o.2, scatter_kws = {'alpha' : 1/3})

Heat maps

Matplotlib's hist2d function

bins_x = np.arange(0.5, 10.5+1, 1)
bins_y = np.arange(-0.5, 10.5+1, 1)
plt.hist2d(data = df, x = 'disc_var1', y = 'disc_var2', bins = [bins_x, bins_y])
plt.colorbar();

Change color palette with the cmap parameter in hist2d
Using cmin to set minimum value for coloring a cell

bins_x = np.arange(0.5, 10.5+1, 1)
bins_y = np.arange(-0.5, 10.5, 1)
plt.hist2d(data = df, x = 'disc_var1', y = 'disc_var2', bins = [bins_x, bins_y], cmap = 'viridis_r', cmin = 0.5)

Add text annotations with the count of points to each cell

counts = h2d[0]

# loop through the cell counts and add text annotations for each
for i in range(counts.shape[0]):
    for j in range(counts.shape[1]):
            c = counts[i, j]
                if c > 7: # increase visibility of text on darkest cells
                    plt.text(bins_x[i]+0.5, bins_y[j]+0.5, int(c), ha = 'center', va = 'center', color = 'white')
                elif c >0:
                    plt.text(bins_x[i]+0.5, bins_y[j]+0.5, int(c), ha = 'center', va = 'center', color = 'black')

Violin plots

Seaborn's violinplot function

sb.violinplot(data = df, x = 'cat_var', y = 'num_var')

\

\
Adapt to monocolor, remove miniature box plot inside violins (inner = None)

base_color = sb.color_palette()[0]
sb.violinplot(data = df, x = 'cat_var', y = 'num_var', color = base_color, inner = None)

\
Horizontal rendering

sb.violinplot(data = df, x = 'num_var', y = 'cat_var', color = base_color, inner = None)

Box plots

\

\
Seaborn's boxplot function

sb.boxplot(data = df, x = 'cat_var', y = 'num_var', color = base_color)
plt. ylim(ax1.get_ylim()) # set y-axis limits to the left subplot's, if there is one

\
Horizontal boxplots

sb.boxplot(data = df, x = 'num_var', y = 'cat_var', color = base_color)

\
Violinplot: Plotting three middle quartiles with inner = 'quartile'

sb.violinplot(data = df, x = 'cat_var', y = 'num_var', color = base_color, inner = 'quartile')

Clustered bar charts

\

\
Create clustered bar chart with seaborn

sb.countplot(data = df, x = 'cat_var1', hue = 'cat_var2')

\
Move legend to x-axis

ax = sb.countplot(data = df, x = 'cat_var1', hue = 'cat_var2')
ax.legend(loc = 8, ncol = 3, framealpha = 1, title = 'cat_var2')

\
Heat maps: Summarization of counts into matrix before plotting
Series reset_index and DataFrame pivot

ct_counts = df.groupby(['cat_var1', 'cat_var2']).size()
ct_counts = ct_counts.reset_index(name = 'count')
ct_counts = ct_counts.pivot(index = 'cat_var2', columns = 'cat_var1', values = 'count')
sb.heatmap(ct_counts)

\

\
Adding annotations to the heatmap using fmt = 'd' for integer output

sb.heatmap(ct_counts, annot = True, fmt = 'd')

Faceting

Seaborn's FacetGrid class

g = sb.FacetGrid(data = df, col = 'cat_var')
g.map(plt.hist, "num_var")


\
Extra visualizations as keyword arguments to the map function

bin_edges = np.arange(-3, df['num_var'].max()+1/3, 1/3)
g = sb.FacetGrid(data = df, col = 'cat_var')
g.map (plt.hist, "num_var", bins = bin_edges)

\
Many categorical levels

group_means = df.groupby(['many_cat_var']).mean()
group_order = group_means.sort_values(['num_var'], ascending = False).index

g = sb.FacetGrid(data = df, col = 'many_cat_var', col_wrap = 5, size = 2, col_order = group_order)
g.map(plt.hist, 'num_var', bins = np.arange(5, 15+1, 1))
g.set_titles('{col_name}')

Adaption of univariate plots

Adapted bar charts
Seaborn's barplot function

base_color = sb.color_palette()[0]
sb.barplot(data = df, x = 'cat_var', y = 'num_var', color = base_color)


\
Seaborn's pointplot function

sb.pointplot(data = df, x = 'cat_var', y = 'num_var', linestyles = "")
plt.ylabel('Avg. value of num_var')


\
Adapted histograms
Bar heights indicate value other than a count by using the "weights" parameter

bin_edges = np.arange(0, df['num_var'].max()+1/3, 1/3)

# count number of points in each bin
bin_idxs = pd.cut(df['num_var'], bin_edges, right = False, include_lowest = True,
                  labels = False).astype(int)
pts_per_bin = df.groupby(bin_idxs).size()

num_var_wts = df['binary_out'] / pts_per_bin[bin_idxs].values

# plot the data using the calculated weights
plt.hist(data = df, x = 'num_var', bins = bin_edges, weights = num_var_wts)
plt.xlabel('num_var')
plt.ylabel('mean(binary_out)')

Line plots

Matplotlib's errorbar function

plt.errorbar(data = df, x = 'num_var1', y = 'num_var2')