Let's first start by developing some intuition of what a complex signal looks like. We can use GNU Radio to generate a signal that's just an unmodulated carrier, and then put that into a UI that will display the real and imaginary components over time:
The result for 80 Hz is this:
Notice how the real part is 90 degrees behind the imaginary part. If you were to plot this on the complex plane, it would trace a circle.
If we change the frequency to -80 Hz:
Now the phase difference is still 90 degrees, but it's the imaginary part that's lagging. Plotted on the complex plane it still traces a circle, but it spins in the opposite direction.
It's this property that allows complex signals to represent positive and negative frequencies.
Now importantly, this 90 degree phase shift holds for any frequency. If we change the frequency to 160 Hz but keep the sample rate the same:
The oscillation speed has doubled, as expected, but the phase difference between the real and imaginary parts is still 90 degrees.
This is why IQ data isn't equivalent to sampling twice as fast: the 90 degree phase relationship between real and imaginary components must exist for every frequency. By sampling the signal twice with some delay between samples, you can indeed introduce a 90 degree phase shift for some frequencies. But 90 degrees is a different amount of time for each frequency, so generating the imaginary component with just a delay will generate the correct results for only one frequency.
Since the imaginary component is just the real component +/- 90 degrees, if we had some kind of filter that could introduce a 90 degree phase shift for every frequency we could use that to convert from a real signal to a complex one.
Mathematically, that "filter" is called the Hilbert transform. It can be realized as an analog filter or a digital filter.
GNU Radio provides a "Hilbert" block which has a real input and complex output. It uses the Hilbert transform to create the imaginary part, where the real part is just the input passed through, with appropriate delay to match the delay added by the filter. We can use this block to take a real-valued signal and produce the equivalent complex-valued signal. The complex signal has (ideally) no negative frequencies present: it is an analytic signal.
It is interesting then to see what happens if we present this block with an input that contains more than one frequency, like a square wave:
Note how the real component is the square wave we expect, but the imaginary part certainly isn't just a delayed square wave. Once the real component is not a single frequency the 90 degree relationship between real and imaginary parts is no longer visually obvious from the time domain plot.
We can see however that the frequency domain is just what we'd expect for a square wave: a fundamental at 640 Hz and then a series of odd harmonics of that. Ideally there would be no negative frequencies present, but the ideal Hilbert filter has an infinite impulse response: truncating it introduces some imaging.
Finally we can take the complex value and split it into real and imaginary parts. We've already seen them in the time domain, but looking at them in the frequency domain we can see that really all the same frequency components are in both real and imaginary parts, just 90 degrees apart:
This visualization shows only the frequency magnitude but not phase, so the real and imaginary parts are drawn right on top of each other. We can also see that the discrete Fourier transform inherently produces complex results, but since we gave it real inputs the negative frequencies are exactly a mirror of the positive ones.
Perhaps now with a better intuition of what we're trying to accomplish with IQ sampling, how might we go about generating the digital stream of complex numbers from an analog signal which can have only real values?
One way would be implement an analog Hilbert filter, and feed that into the 2nd channel of an ADC. We can then treat one channel as the real part, and the other channel as the imaginary part.
However there would be little point in that: to realize an analog Hilbert filter that provides an accurate 90 degree phase shift over a wide range of frequencies requires a large number of components, and the filter can't add any information. This approach is used in some analog SSB transceivers for sideband cancellation, but if you're going to be digitizing the signal then a digital implementation would be cheaper and perform better.
Instead, we can feed the RF signal to not one but two frequency mixers:
simulate this circuit – Schematic created using CircuitLab
You've probably read about how frequency mixers produce outputs with the sum and difference of the frequency components at the inputs. That's true, but what's the phase of the outputs? Turns out if you change the phase of the LO, then the phase of all the outputs is changed by the same amount. And unlike a delay, modulating the phase in this way makes the same phase shift for all frequencies, just what we need to generate both real and imaginary parts for a complex signal.
It is simple (in terms of component complexity) to create this phase shift with a mixer because the mixer is a nonlinear device. That means it has access to mathematical operators that linear devices (capacitors, inductors, resistors, transmission lines) don't, namely the multiplication of two functions.
Furthermore, since both real and imaginary parts are available digitally, we don't need analog filters to deal with image cancellation. What would be considered "images frequencies" in an analog design are instead just negative frequencies in the digital domain, and since the signal can be manipulated as a complex number these negative frequencies don't present any ambiguity.
This is also why you can find direct-sampling SDRs that work up to a few hundred MHz, but they get pricey because an ADC operating at 1 Gsps isn't cheap, nor is the FPGA you'll need to process that data rate. Once the frequency becomes high enough that a mixer is required, SDRs are almost exclusively use an IQ architecture since it's simpler to implement.